summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/block_builder.cc370
-rw-r--r--compiler/optimizing/block_builder.h88
-rw-r--r--compiler/optimizing/builder.cc2823
-rw-r--r--compiler/optimizing/builder.h348
-rw-r--r--compiler/optimizing/bytecode_utils.h179
-rw-r--r--compiler/optimizing/code_generator.cc20
-rw-r--r--compiler/optimizing/code_generator.h3
-rw-r--r--compiler/optimizing/code_generator_arm.cc67
-rw-r--r--compiler/optimizing/code_generator_arm.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc69
-rw-r--r--compiler/optimizing/code_generator_arm64.h2
-rw-r--r--compiler/optimizing/code_generator_mips.cc68
-rw-r--r--compiler/optimizing/code_generator_mips.h2
-rw-r--r--compiler/optimizing/code_generator_mips64.cc68
-rw-r--r--compiler/optimizing/code_generator_mips64.h2
-rw-r--r--compiler/optimizing/code_generator_x86.cc67
-rw-r--r--compiler/optimizing/code_generator_x86.h2
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc67
-rw-r--r--compiler/optimizing/code_generator_x86_64.h2
-rw-r--r--compiler/optimizing/common_arm64.h14
-rw-r--r--compiler/optimizing/constant_folding_test.cc477
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc100
-rw-r--r--compiler/optimizing/graph_checker.cc69
-rw-r--r--compiler/optimizing/gvn_test.cc2
-rw-r--r--compiler/optimizing/inliner.cc15
-rw-r--r--compiler/optimizing/instruction_builder.cc2681
-rw-r--r--compiler/optimizing/instruction_builder.h303
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc17
-rw-r--r--compiler/optimizing/intrinsics_mips.cc5
-rw-r--r--compiler/optimizing/intrinsics_x86.cc4
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc4
-rw-r--r--compiler/optimizing/live_ranges_test.cc14
-rw-r--r--compiler/optimizing/liveness_test.cc16
-rw-r--r--compiler/optimizing/nodes.cc167
-rw-r--r--compiler/optimizing/nodes.h368
-rw-r--r--compiler/optimizing/optimizing_compiler.cc10
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h6
-rw-r--r--compiler/optimizing/optimizing_unit_test.h7
-rw-r--r--compiler/optimizing/pretty_printer_test.cc178
-rw-r--r--compiler/optimizing/ssa_builder.cc412
-rw-r--r--compiler/optimizing/ssa_builder.h71
-rw-r--r--compiler/optimizing/ssa_test.cc22
-rw-r--r--compiler/optimizing/stack_map_stream.cc58
-rw-r--r--compiler/optimizing/stack_map_stream.h3
-rw-r--r--compiler/optimizing/stack_map_test.cc74
45 files changed, 4505 insertions, 4841 deletions
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc
new file mode 100644
index 0000000000..5e70a8284d
--- /dev/null
+++ b/compiler/optimizing/block_builder.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "block_builder.h"
+
+#include "bytecode_utils.h"
+
+namespace art {
+
+HBasicBlock* HBasicBlockBuilder::MaybeCreateBlockAt(uint32_t dex_pc) {
+ return MaybeCreateBlockAt(dex_pc, dex_pc);
+}
+
+HBasicBlock* HBasicBlockBuilder::MaybeCreateBlockAt(uint32_t semantic_dex_pc,
+ uint32_t store_dex_pc) {
+ HBasicBlock* block = branch_targets_[store_dex_pc];
+ if (block == nullptr) {
+ block = new (arena_) HBasicBlock(graph_, semantic_dex_pc);
+ branch_targets_[store_dex_pc] = block;
+ }
+ DCHECK_EQ(block->GetDexPc(), semantic_dex_pc);
+ return block;
+}
+
+bool HBasicBlockBuilder::CreateBranchTargets() {
+ // Create the first block for the dex instructions, single successor of the entry block.
+ MaybeCreateBlockAt(0u);
+
+ if (code_item_.tries_size_ != 0) {
+ // 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_;
+ MaybeCreateBlockAt(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.
+ MaybeCreateBlockAt(dex_pc_end);
+ } else if (dex_pc_end == code_item_.insns_size_in_code_units_) {
+ // The TryItem spans until the very end of the CodeItem and therefore
+ // cannot have any code afterwards.
+ } else {
+ // The TryItem spans beyond the end of the CodeItem. This is invalid code.
+ return false;
+ }
+ }
+
+ // 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()) {
+ MaybeCreateBlockAt(iterator.GetHandlerAddress());
+ }
+ handlers_ptr = iterator.EndDataPointer();
+ }
+ }
+
+ // Iterate over all instructions and find branching instructions. Create blocks for
+ // the locations these instructions branch to.
+ for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
+ uint32_t dex_pc = it.CurrentDexPc();
+ const Instruction& instruction = it.CurrentInstruction();
+
+ if (instruction.IsBranch()) {
+ number_of_branches_++;
+ MaybeCreateBlockAt(dex_pc + instruction.GetTargetOffset());
+ } else if (instruction.IsSwitch()) {
+ DexSwitchTable table(instruction, dex_pc);
+ for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) {
+ MaybeCreateBlockAt(dex_pc + s_it.CurrentTargetOffset());
+
+ // Create N-1 blocks where we will insert comparisons of the input value
+ // against the Switch's case keys.
+ if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) {
+ // Store the block under dex_pc of the current key at the switch data
+ // instruction for uniqueness but give it the dex_pc of the SWITCH
+ // instruction which it semantically belongs to.
+ MaybeCreateBlockAt(dex_pc, s_it.GetDexPcForCurrentIndex());
+ }
+ }
+ } else if (instruction.Opcode() == Instruction::MOVE_EXCEPTION) {
+ // End the basic block after MOVE_EXCEPTION. This simplifies the later
+ // stage of TryBoundary-block insertion.
+ } else {
+ continue;
+ }
+
+ if (instruction.CanFlowThrough()) {
+ if (it.IsLast()) {
+ // 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 {
+ MaybeCreateBlockAt(dex_pc + it.CurrentInstruction().SizeInCodeUnits());
+ }
+ }
+ }
+
+ return true;
+}
+
+void HBasicBlockBuilder::ConnectBasicBlocks() {
+ HBasicBlock* block = graph_->GetEntryBlock();
+ graph_->AddBlock(block);
+
+ bool is_throwing_block = false;
+ for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
+ uint32_t dex_pc = it.CurrentDexPc();
+
+ // Check if this dex_pc address starts a new basic block.
+ HBasicBlock* next_block = GetBlockAt(dex_pc);
+ if (next_block != nullptr) {
+ if (block != nullptr) {
+ // Last instruction did not end its basic block but a new one starts here.
+ // It must have been a block falling through into the next one.
+ block->AddSuccessor(next_block);
+ }
+ block = next_block;
+ is_throwing_block = false;
+ graph_->AddBlock(block);
+ }
+
+ if (block == nullptr) {
+ // Ignore dead code.
+ continue;
+ }
+
+ const Instruction& instruction = it.CurrentInstruction();
+
+ if (!is_throwing_block && IsThrowingDexInstruction(instruction)) {
+ DCHECK(!ContainsElement(throwing_blocks_, block));
+ is_throwing_block = true;
+ throwing_blocks_.push_back(block);
+ }
+
+ if (instruction.IsBranch()) {
+ uint32_t target_dex_pc = dex_pc + instruction.GetTargetOffset();
+ block->AddSuccessor(GetBlockAt(target_dex_pc));
+ } else if (instruction.IsReturn() || (instruction.Opcode() == Instruction::THROW)) {
+ block->AddSuccessor(graph_->GetExitBlock());
+ } else if (instruction.IsSwitch()) {
+ DexSwitchTable table(instruction, dex_pc);
+ for (DexSwitchTableIterator s_it(table); !s_it.Done(); s_it.Advance()) {
+ uint32_t target_dex_pc = dex_pc + s_it.CurrentTargetOffset();
+ block->AddSuccessor(GetBlockAt(target_dex_pc));
+
+ if (table.ShouldBuildDecisionTree() && !s_it.IsLast()) {
+ uint32_t next_case_dex_pc = s_it.GetDexPcForCurrentIndex();
+ HBasicBlock* next_case_block = GetBlockAt(next_case_dex_pc);
+ block->AddSuccessor(next_case_block);
+ block = next_case_block;
+ graph_->AddBlock(block);
+ }
+ }
+ } else {
+ // Remaining code only applies to instructions which end their basic block.
+ continue;
+ }
+
+ if (instruction.CanFlowThrough()) {
+ uint32_t next_dex_pc = dex_pc + instruction.SizeInCodeUnits();
+ block->AddSuccessor(GetBlockAt(next_dex_pc));
+ }
+
+ // The basic block ends here. Do not add any more instructions.
+ block = nullptr;
+ }
+
+ graph_->AddBlock(graph_->GetExitBlock());
+}
+
+// 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;
+}
+
+// Iterates over the exception handlers of `try_item`, finds the corresponding
+// catch blocks and makes them successors of `try_boundary`. The order of
+// successors matches the order in which runtime exception delivery searches
+// for a handler.
+static void LinkToCatchBlocks(HTryBoundary* try_boundary,
+ const DexFile::CodeItem& code_item,
+ const DexFile::TryItem* try_item,
+ const ArenaSafeMap<uint32_t, HBasicBlock*>& catch_blocks) {
+ for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
+ try_boundary->AddExceptionHandler(catch_blocks.Get(it.GetHandlerAddress()));
+ }
+}
+
+bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_block) {
+ if (kIsDebugBuild) {
+ DCHECK_NE(catch_block->GetDexPc(), kNoDexPc) << "Should not be called on synthetic blocks";
+ DCHECK(!graph_->GetEntryBlock()->GetSuccessors().empty())
+ << "Basic blocks must have been created and connected";
+ for (HBasicBlock* predecessor : catch_block->GetPredecessors()) {
+ DCHECK(!predecessor->IsSingleTryBoundary())
+ << "TryBoundary blocks must not have not been created yet";
+ }
+ }
+
+ const Instruction& first = GetDexInstructionAt(code_item_, catch_block->GetDexPc());
+ if (first.Opcode() == Instruction::MOVE_EXCEPTION) {
+ // Verifier guarantees that if a catch block begins with MOVE_EXCEPTION then
+ // it has no live normal predecessors.
+ return false;
+ } else if (catch_block->GetPredecessors().empty()) {
+ // Normal control-flow edges have already been created. Since block's list of
+ // predecessors is empty, it cannot have any live or dead normal predecessors.
+ return false;
+ }
+
+ // The catch block has normal predecessors but we do not know which are live
+ // and which will be removed during the initial DCE. Return `true` to signal
+ // that it may have live normal predecessors.
+ return true;
+}
+
+void HBasicBlockBuilder::InsertTryBoundaryBlocks() {
+ 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.
+ for (HBasicBlock* block : graph_->GetBlocks()) {
+ if (block->GetDexPc() == kNoDexPc) {
+ continue;
+ }
+
+ // 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 (ContainsElement(throwing_blocks_, block)) {
+ // Try to find a TryItem covering the block.
+ 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.
+ try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(code_item_, try_item_idx));
+ }
+ }
+ }
+
+ // Map from a handler dex_pc to the corresponding catch block.
+ ArenaSafeMap<uint32_t, HBasicBlock*> catch_blocks(
+ std::less<uint32_t>(), arena_->Adapter(kArenaAllocGraphBuilder));
+
+ // Iterate over catch blocks, create artifical landing pads if necessary to
+ // simplify the CFG, and set metadata.
+ 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();
+ if (catch_blocks.find(address) != catch_blocks.end()) {
+ // Catch block already processed.
+ continue;
+ }
+
+ // Check if we should create an artifical landing pad for the catch block.
+ // We create one if the catch block is also a try block because we do not
+ // have a strategy for inserting TryBoundaries on exceptional edges.
+ // We also create one if the block might have normal predecessors so as to
+ // simplify register allocation.
+ HBasicBlock* catch_block = GetBlockAt(address);
+ bool is_try_block = (try_block_info.find(catch_block->GetBlockId()) != try_block_info.end());
+ if (is_try_block || MightHaveLiveNormalPredecessors(catch_block)) {
+ HBasicBlock* new_catch_block = new (arena_) HBasicBlock(graph_, address);
+ new_catch_block->AddInstruction(new (arena_) HGoto(address));
+ new_catch_block->AddSuccessor(catch_block);
+ graph_->AddBlock(new_catch_block);
+ catch_block = new_catch_block;
+ }
+
+ catch_blocks.Put(address, catch_block);
+ catch_block->SetTryCatchInformation(
+ new (arena_) TryCatchInformation(iterator.GetHandlerTypeIndex(), *dex_file_));
+ }
+ handlers_ptr = iterator.EndDataPointer();
+ }
+
+ // 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, catch_blocks);
+ 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;
+ }
+
+ // 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, catch_blocks);
+ }
+ }
+}
+
+bool HBasicBlockBuilder::Build() {
+ DCHECK(graph_->GetBlocks().empty());
+
+ graph_->SetEntryBlock(new (arena_) HBasicBlock(graph_, kNoDexPc));
+ graph_->SetExitBlock(new (arena_) HBasicBlock(graph_, kNoDexPc));
+
+ // TODO(dbrazdil): Do CreateBranchTargets and ConnectBasicBlocks in one pass.
+ if (!CreateBranchTargets()) {
+ return false;
+ }
+
+ ConnectBasicBlocks();
+ InsertTryBoundaryBlocks();
+
+ return true;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h
new file mode 100644
index 0000000000..1be0b4ce98
--- /dev/null
+++ b/compiler/optimizing/block_builder.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
+#define ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
+
+#include "base/arena_containers.h"
+#include "base/arena_object.h"
+#include "dex_file.h"
+#include "nodes.h"
+
+namespace art {
+
+class HBasicBlockBuilder : public ValueObject {
+ public:
+ HBasicBlockBuilder(HGraph* graph,
+ const DexFile* const dex_file,
+ const DexFile::CodeItem& code_item)
+ : arena_(graph->GetArena()),
+ graph_(graph),
+ dex_file_(dex_file),
+ code_item_(code_item),
+ branch_targets_(code_item.insns_size_in_code_units_,
+ nullptr,
+ arena_->Adapter(kArenaAllocGraphBuilder)),
+ throwing_blocks_(kDefaultNumberOfThrowingBlocks, arena_->Adapter(kArenaAllocGraphBuilder)),
+ number_of_branches_(0u) {}
+
+ // Creates basic blocks in `graph_` at branch target dex_pc positions of the
+ // `code_item_`. Blocks are connected but left unpopulated with instructions.
+ // TryBoundary blocks are inserted at positions where control-flow enters/
+ // exits a try block.
+ bool Build();
+
+ size_t GetNumberOfBranches() const { return number_of_branches_; }
+ HBasicBlock* GetBlockAt(uint32_t dex_pc) const { return branch_targets_[dex_pc]; }
+
+ private:
+ // Creates a basic block starting at given `dex_pc`.
+ HBasicBlock* MaybeCreateBlockAt(uint32_t dex_pc);
+
+ // Creates a basic block for bytecode instructions at `semantic_dex_pc` and
+ // stores it under the `store_dex_pc` key. This is used when multiple blocks
+ // share the same semantic dex_pc, e.g. when building switch decision trees.
+ HBasicBlock* MaybeCreateBlockAt(uint32_t semantic_dex_pc, uint32_t store_dex_pc);
+
+ bool CreateBranchTargets();
+ void ConnectBasicBlocks();
+ void InsertTryBoundaryBlocks();
+
+ // Helper method which decides whether `catch_block` may have live normal
+ // predecessors and thus whether a synthetic catch block needs to be created
+ // to avoid mixing normal and exceptional predecessors.
+ // Should only be called during InsertTryBoundaryBlocks on blocks at catch
+ // handler dex_pcs.
+ bool MightHaveLiveNormalPredecessors(HBasicBlock* catch_block);
+
+ ArenaAllocator* const arena_;
+ HGraph* const graph_;
+
+ const DexFile* const dex_file_;
+ const DexFile::CodeItem& code_item_;
+
+ ArenaVector<HBasicBlock*> branch_targets_;
+ ArenaVector<HBasicBlock*> throwing_blocks_;
+ size_t number_of_branches_;
+
+ static constexpr size_t kDefaultNumberOfThrowingBlocks = 2u;
+
+ DISALLOW_COPY_AND_ASSIGN(HBasicBlockBuilder);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index b6b8322f03..86742e6526 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -20,148 +20,49 @@
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
#include "base/logging.h"
-#include "class_linker.h"
#include "dex/verified_method.h"
-#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
-#include "dex/verified_method.h"
-#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "nodes.h"
#include "primitive.h"
-#include "scoped_thread_state_change.h"
-#include "ssa_builder.h"
#include "thread.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace art {
-void HGraphBuilder::InitializeLocals(uint16_t count) {
- graph_->SetNumberOfVRegs(count);
- locals_.resize(count);
- for (int i = 0; i < count; i++) {
- HLocal* local = new (arena_) HLocal(i);
- entry_block_->AddInstruction(local);
- locals_[i] = local;
- }
-}
-
-void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
- // dex_compilation_unit_ is null only when unit testing.
- if (dex_compilation_unit_ == nullptr) {
- return;
- }
-
- graph_->SetNumberOfInVRegs(number_of_parameters);
- const char* shorty = dex_compilation_unit_->GetShorty();
- int locals_index = locals_.size() - number_of_parameters;
- int parameter_index = 0;
-
- const DexFile::MethodId& referrer_method_id =
- dex_file_->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
- if (!dex_compilation_unit_->IsStatic()) {
- // Add the implicit 'this' argument, not expressed in the signature.
- HParameterValue* parameter = new (arena_) HParameterValue(*dex_file_,
- referrer_method_id.class_idx_,
- parameter_index++,
- Primitive::kPrimNot,
- true);
- entry_block_->AddInstruction(parameter);
- HLocal* local = GetLocalAt(locals_index++);
- entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
- number_of_parameters--;
- }
-
- const DexFile::ProtoId& proto = dex_file_->GetMethodPrototype(referrer_method_id);
- const DexFile::TypeList* arg_types = dex_file_->GetProtoParameters(proto);
- for (int i = 0, shorty_pos = 1; i < number_of_parameters; i++) {
- HParameterValue* parameter = new (arena_) HParameterValue(
- *dex_file_,
- arg_types->GetTypeItem(shorty_pos - 1).type_idx_,
- parameter_index++,
- Primitive::GetType(shorty[shorty_pos]),
- false);
- ++shorty_pos;
- 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()));
- bool is_wide = (parameter->GetType() == Primitive::kPrimLong)
- || (parameter->GetType() == Primitive::kPrimDouble);
- if (is_wide) {
- i++;
- locals_index++;
- parameter_index++;
- }
- }
-}
-
-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;
-}
-
void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
if (compilation_stats_ != nullptr) {
compilation_stats_->RecordStat(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,2707 +70,39 @@ 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);
- }
- }
-}
-
-GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item,
- StackHandleScopeCollection* handles) {
+GraphAnalysisResult HGraphBuilder::BuildGraph() {
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;
- }
+ graph_->SetNumberOfVRegs(code_item_.registers_size_);
+ graph_->SetNumberOfInVRegs(code_item_.ins_size_);
+ graph_->SetMaximumNumberOfOutVRegs(code_item_.outs_size_);
+ graph_->SetHasTryCatch(code_item_.tries_size_ != 0);
- // Note that the compiler driver is null when unit testing.
- if ((compiler_driver_ != nullptr) && SkipCompilation(code_item, number_of_branches)) {
+ // 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;
}
- // 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.
- const bool native_debuggable = compiler_driver_ != nullptr &&
- compiler_driver_->GetCompilerOptions().GetNativeDebuggable();
- ArenaBitVector* native_debug_info_locations;
- if (native_debuggable) {
- 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);
+ // 2) Decide whether to skip this method based on its code size and number
+ // of branches.
+ if (SkipCompilation(block_builder_.GetNumberOfBranches())) {
+ return kAnalysisSkipped;
}
- CreateBlocksForTryCatch(code_item);
-
- InitializeParameters(code_item.ins_size_);
-
- 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)) {
- if (current_block_ != nullptr) {
- current_block_->AddInstruction(new (arena_) HNativeDebugInfo(dex_pc));
- }
- }
- if (!AnalyzeDexInstruction(instruction, dex_pc)) {
- return kAnalysisInvalidBytecode;
- }
- 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_);
-
- // 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);
-
+ // 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;
- }
-
- 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;
-}
-
-void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_item,
- ArenaBitVector* locations) {
- // The callback gets called when the line number changes.
- // In other words, it marks the start of new java statement.
- struct Callback {
- static bool Position(void* ctx, const DexFile::PositionInfo& entry) {
- static_cast<ArenaBitVector*>(ctx)->SetBit(entry.address_);
- return false;
- }
- };
- 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_);
- 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_));
- const Instruction* next = inst->Next();
- if (next < end) {
- locations->SetBit(next->GetDexPc(code_item.insns_));
- }
- break;
- }
- default:
- break;
- }
- }
-}
-
-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,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
- current_block_->AddInstruction(new (arena_) T(type, first, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-void HGraphBuilder::Conversion_12x(const Instruction& instruction,
- Primitive::Type input_type,
- Primitive::Type result_type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), input_type, dex_pc);
- current_block_->AddInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-template<typename T>
-void HGraphBuilder::Binop_23x(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
- HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
- current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-template<typename T>
-void HGraphBuilder::Binop_23x_shift(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
- HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt, dex_pc);
- current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-void HGraphBuilder::Binop_23x_cmp(const Instruction& instruction,
- Primitive::Type type,
- ComparisonBias bias,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), type, dex_pc);
- HInstruction* second = LoadLocal(instruction.VRegC(), type, dex_pc);
- current_block_->AddInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-template<typename T>
-void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
- HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
- current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-template<typename T>
-void HGraphBuilder::Binop_12x(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegA(), type, dex_pc);
- HInstruction* second = LoadLocal(instruction.VRegB(), type, dex_pc);
- current_block_->AddInstruction(new (arena_) T(type, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-template<typename T>
-void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
- HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s(), dex_pc);
- if (reverse) {
- std::swap(first, second);
- }
- current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-template<typename T>
-void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
- HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
- HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b(), dex_pc);
- if (reverse) {
- std::swap(first, second);
- }
- current_block_->AddInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
-}
-
-static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
- Thread* self = Thread::Current();
- return cu->IsConstructor()
- && driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
-}
-
-void HGraphBuilder::BuildReturn(const Instruction& instruction,
- Primitive::Type type,
- uint32_t dex_pc) {
- if (type == Primitive::kPrimVoid) {
- if (graph_->ShouldGenerateConstructorBarrier()) {
- // The compilation unit is null during testing.
- if (dex_compilation_unit_ != nullptr) {
- DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
- << "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
- }
- current_block_->AddInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
- }
- current_block_->AddInstruction(new (arena_) HReturnVoid(dex_pc));
- } else {
- 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;
-}
-
-static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
- switch (opcode) {
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_STATIC_RANGE:
- return kStatic;
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_DIRECT_RANGE:
- return kDirect;
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_QUICK:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
- return kVirtual;
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_INTERFACE_RANGE:
- return kInterface;
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_SUPER:
- return kSuper;
- default:
- LOG(FATAL) << "Unexpected invoke opcode: " << opcode;
- UNREACHABLE();
- }
-}
-
-ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
-
- ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
-
- ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
- *dex_compilation_unit_->GetDexFile(),
- method_idx,
- dex_compilation_unit_->GetDexCache(),
- class_loader,
- /* referrer */ nullptr,
- invoke_type);
-
- if (UNLIKELY(resolved_method == nullptr)) {
- // Clean up any exception left by type resolution.
- soa.Self()->ClearException();
- return nullptr;
- }
-
- // Check access. The class linker has a fast path for looking into the dex cache
- // and does not check the access if it hits it.
- if (compiling_class.Get() == nullptr) {
- if (!resolved_method->IsPublic()) {
- return nullptr;
- }
- } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
- resolved_method,
- dex_compilation_unit_->GetDexCache().Get(),
- method_idx)) {
- return nullptr;
- }
-
- // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
- // We need to look at the referrer's super class vtable. We need to do this to know if we need to
- // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
- // which require runtime handling.
- if (invoke_type == kSuper) {
- if (compiling_class.Get() == nullptr) {
- // We could not determine the method's class we need to wait until runtime.
- DCHECK(Runtime::Current()->IsAotCompiler());
- return nullptr;
- }
- ArtMethod* current_method = graph_->GetArtMethod();
- DCHECK(current_method != nullptr);
- Handle<mirror::Class> methods_class(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
- method_idx,
- current_method)));
- if (methods_class.Get() == nullptr) {
- // Invoking a super method requires knowing the actual super class. If we did not resolve
- // the compiling method's declaring class (which only happens for ahead of time
- // compilation), bail out.
- DCHECK(Runtime::Current()->IsAotCompiler());
- return nullptr;
- } else {
- ArtMethod* actual_method;
- if (methods_class->IsInterface()) {
- actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
- resolved_method, class_linker->GetImagePointerSize());
- } else {
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
- vtable_index, class_linker->GetImagePointerSize());
- }
- if (actual_method != resolved_method &&
- !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
- // The back-end code generator relies on this check in order to ensure that it will not
- // attempt to read the dex_cache with a dex_method_index that is not from the correct
- // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
- // builder, which means that the code-generator (and compiler driver during sharpening and
- // inliner, maybe) might invoke an incorrect method.
- // TODO: The actual method could still be referenced in the current dex file, so we
- // could try locating it.
- // TODO: Remove the dex_file restriction.
- return nullptr;
- }
- if (!actual_method->IsInvokable()) {
- // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
- // could resolve the callee to the wrong method.
- return nullptr;
- }
- resolved_method = actual_method;
- }
- }
-
- // Check for incompatible class changes. The class linker has a fast path for
- // looking into the dex cache and does not check incompatible class changes if it hits it.
- if (resolved_method->CheckIncompatibleClassChange(invoke_type)) {
- return nullptr;
- }
-
- return resolved_method;
-}
-
-bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
- uint32_t dex_pc,
- uint32_t method_idx,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index) {
- InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
- const char* descriptor = dex_file_->GetMethodShorty(method_idx);
- Primitive::Type return_type = Primitive::GetType(descriptor[0]);
-
- // Remove the return type from the 'proto'.
- size_t number_of_arguments = strlen(descriptor) - 1;
- if (invoke_type != kStatic) { // instance call
- // One extra argument for 'this'.
- number_of_arguments++;
- }
-
- MethodReference target_method(dex_file_, method_idx);
-
- // Special handling for string init.
- int32_t string_init_offset = 0;
- bool is_string_init = compiler_driver_->IsStringInit(method_idx,
- dex_file_,
- &string_init_offset);
- // Replace calls to String.<init> with StringFactory.
- if (is_string_init) {
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
- HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- dchecked_integral_cast<uint64_t>(string_init_offset),
- 0U
- };
- HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
- arena_,
- number_of_arguments - 1,
- Primitive::kPrimNot /*return_type */,
- dex_pc,
- method_idx,
- target_method,
- dispatch_info,
- invoke_type,
- kStatic /* optimized_invoke_type */,
- HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
- return HandleStringInit(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor);
- }
-
- ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
-
- if (UNLIKELY(resolved_method == nullptr)) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
- HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- invoke_type);
- return HandleInvoke(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- nullptr /* clinit_check */);
- }
-
- // Potential class initialization check, in the case of a static method call.
- HClinitCheck* clinit_check = nullptr;
- HInvoke* invoke = nullptr;
- if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) {
- // By default, consider that the called method implicitly requires
- // an initialization check of its declaring method.
- HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement
- = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
- ScopedObjectAccess soa(Thread::Current());
- if (invoke_type == kStatic) {
- clinit_check = ProcessClinitCheckForInvoke(
- dex_pc, resolved_method, method_idx, &clinit_check_requirement);
- } else if (invoke_type == kSuper) {
- if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
- // Update the target method to the one resolved. Note that this may be a no-op if
- // we resolved to the method referenced by the instruction.
- method_idx = resolved_method->GetDexMethodIndex();
- target_method = MethodReference(dex_file_, method_idx);
- }
- }
-
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
- HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
- HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
- 0u,
- 0U
- };
- invoke = new (arena_) HInvokeStaticOrDirect(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- target_method,
- dispatch_info,
- invoke_type,
- invoke_type,
- clinit_check_requirement);
- } else if (invoke_type == kVirtual) {
- ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
- invoke = new (arena_) HInvokeVirtual(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- resolved_method->GetMethodIndex());
- } else {
- DCHECK_EQ(invoke_type, kInterface);
- ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
- invoke = new (arena_) HInvokeInterface(arena_,
- number_of_arguments,
- return_type,
- dex_pc,
- method_idx,
- resolved_method->GetDexMethodIndex());
- }
-
- return HandleInvoke(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- clinit_check);
-}
-
-bool HGraphBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
- bool finalizable;
- bool can_throw = NeedsAccessCheck(type_index, &finalizable);
-
- // Only the non-resolved entrypoint handles the finalizable class case. If we
- // need access checks, then we haven't resolved the method and the class may
- // again be finalizable.
- QuickEntrypointEnum entrypoint = (finalizable || can_throw)
- ? kQuickAllocObject
- : kQuickAllocObjectInitialized;
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
-
- if (outer_dex_cache.Get() != dex_cache.Get()) {
- // We currently do not support inlining allocations across dex files.
- return false;
- }
-
- HLoadClass* load_class = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- type_index,
- outer_dex_file,
- IsOutermostCompilingClass(type_index),
- dex_pc,
- /*needs_access_check*/ can_throw,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index));
-
- current_block_->AddInstruction(load_class);
- HInstruction* cls = load_class;
- if (!IsInitialized(resolved_class)) {
- cls = new (arena_) HClinitCheck(load_class, dex_pc);
- current_block_->AddInstruction(cls);
- }
-
- current_block_->AddInstruction(new (arena_) HNewInstance(
- cls,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- can_throw,
- finalizable,
- entrypoint));
- return true;
-}
-
-static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class);
-}
-
-bool HGraphBuilder::IsInitialized(Handle<mirror::Class> cls) const {
- if (cls.Get() == nullptr) {
- return false;
- }
-
- // `CanAssumeClassIsLoaded` will return true if we're JITting, or will
- // check whether the class is in an image for the AOT compilation.
- if (cls->IsInitialized() &&
- compiler_driver_->CanAssumeClassIsLoaded(cls.Get())) {
- return true;
- }
-
- if (IsSubClass(GetOutermostCompilingClass(), cls.Get())) {
- return true;
- }
-
- // TODO: We should walk over the inlined methods, but we don't pass
- // that information to the builder.
- if (IsSubClass(GetCompilingClass(), cls.Get())) {
- return true;
- }
-
- return false;
-}
-
-HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke(
- uint32_t dex_pc,
- ArtMethod* resolved_method,
- uint32_t method_idx,
- HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Thread* self = Thread::Current();
- StackHandleScope<4> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- self, *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(
- self, outer_dex_file)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
- Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass()));
-
- // The index at which the method's class is stored in the DexCache's type array.
- uint32_t storage_index = DexFile::kDexNoIndex;
- bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
- if (is_outer_class) {
- storage_index = outer_class->GetDexTypeIndex();
- } else if (outer_dex_cache.Get() == dex_cache.Get()) {
- // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
- compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
- GetCompilingClass(),
- resolved_method,
- method_idx,
- &storage_index);
- }
-
- HClinitCheck* clinit_check = nullptr;
-
- if (IsInitialized(resolved_method_class)) {
- *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
- } else if (storage_index != DexFile::kDexNoIndex) {
- *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
- HLoadClass* load_class = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- storage_index,
- outer_dex_file,
- is_outer_class,
- dex_pc,
- /*needs_access_check*/ false,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index));
- current_block_->AddInstruction(load_class);
- clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
- current_block_->AddInstruction(clinit_check);
- }
- return clinit_check;
-}
-
-bool HGraphBuilder::SetupInvokeArguments(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- size_t start_index,
- size_t* argument_index) {
- uint32_t descriptor_index = 1; // Skip the return type.
- uint32_t dex_pc = invoke->GetDexPc();
-
- for (size_t i = start_index;
- // Make sure we don't go over the expected arguments or over the number of
- // dex registers given. If the instruction was seen as dead by the verifier,
- // it hasn't been properly checked.
- (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments());
- i++, (*argument_index)++) {
- Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
- bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
- if (!is_range
- && is_wide
- && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) {
- // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
- // reject any class where this is violated. However, the verifier only does these checks
- // on non trivially dead instructions, so we just bailout the compilation.
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because of non-sequential dex register pair in wide argument";
- MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
- return false;
- }
- HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
- invoke->SetArgumentAt(*argument_index, arg);
- if (is_wide) {
- i++;
- }
- }
-
- if (*argument_index != invoke->GetNumberOfArguments()) {
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because of wrong number of arguments in invoke instruction";
- MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
- return false;
- }
-
- if (invoke->IsInvokeStaticOrDirect() &&
- HInvokeStaticOrDirect::NeedsCurrentMethodInput(
- invoke->AsInvokeStaticOrDirect()->GetMethodLoadKind())) {
- invoke->SetArgumentAt(*argument_index, graph_->GetCurrentMethod());
- (*argument_index)++;
- }
-
- return true;
-}
-
-bool HGraphBuilder::HandleInvoke(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- HClinitCheck* clinit_check) {
- DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit());
-
- size_t start_index = 0;
- size_t argument_index = 0;
- if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) { // Instance call.
- HInstruction* arg = LoadLocal(
- is_range ? register_index : args[0], Primitive::kPrimNot, invoke->GetDexPc());
- HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
- current_block_->AddInstruction(null_check);
- invoke->SetArgumentAt(0, null_check);
- start_index = 1;
- argument_index = 1;
- }
-
- if (!SetupInvokeArguments(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- start_index,
- &argument_index)) {
- return false;
- }
-
- if (clinit_check != nullptr) {
- // Add the class initialization check as last input of `invoke`.
- DCHECK(invoke->IsInvokeStaticOrDirect());
- DCHECK(invoke->AsInvokeStaticOrDirect()->GetClinitCheckRequirement()
- == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit);
- invoke->SetArgumentAt(argument_index, clinit_check);
- argument_index++;
- }
-
- current_block_->AddInstruction(invoke);
- latest_result_ = invoke;
-
- return true;
-}
-
-bool HGraphBuilder::HandleStringInit(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor) {
- DCHECK(invoke->IsInvokeStaticOrDirect());
- DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
-
- size_t start_index = 1;
- size_t argument_index = 0;
- if (!SetupInvokeArguments(invoke,
- number_of_vreg_arguments,
- args,
- register_index,
- is_range,
- descriptor,
- start_index,
- &argument_index)) {
- return false;
- }
-
- // Add move-result for StringFactory method.
- uint32_t orig_this_reg = is_range ? register_index : args[0];
- HInstruction* new_instance = LoadLocal(orig_this_reg, Primitive::kPrimNot, invoke->GetDexPc());
- invoke->SetArgumentAt(argument_index, new_instance);
- current_block_->AddInstruction(invoke);
-
- latest_result_ = invoke;
- return true;
-}
-
-static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) {
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- const char* type = dex_file.GetFieldTypeDescriptor(field_id);
- return Primitive::GetType(type[0]);
-}
-
-bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put) {
- uint32_t source_or_dest_reg = instruction.VRegA_22c();
- uint32_t obj_reg = instruction.VRegB_22c();
- uint16_t field_index;
- if (instruction.IsQuickened()) {
- if (!CanDecodeQuickenedInfo()) {
- return false;
- }
- field_index = LookupQuickenedInfo(dex_pc);
- } else {
- field_index = instruction.VRegC_22c();
- }
-
- ScopedObjectAccess soa(Thread::Current());
- ArtField* resolved_field =
- compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
-
-
- HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot, dex_pc);
- HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
- current_block_->AddInstruction(null_check);
-
- Primitive::Type field_type = (resolved_field == nullptr)
- ? GetFieldAccessType(*dex_file_, field_index)
- : resolved_field->GetTypeAsPrimitiveType();
- if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
- HInstruction* field_set = nullptr;
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
- value,
- field_type,
- field_index,
- dex_pc);
- } else {
- uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
- field_set = new (arena_) HInstanceFieldSet(null_check,
- value,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_compilation_unit_->GetDexCache(),
- dex_pc);
- }
- current_block_->AddInstruction(field_set);
- } else {
- HInstruction* field_get = nullptr;
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
- field_type,
- field_index,
- dex_pc);
- } else {
- uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
- field_get = new (arena_) HInstanceFieldGet(null_check,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_compilation_unit_->GetDexCache(),
- dex_pc);
- }
- current_block_->AddInstruction(field_get);
- UpdateLocal(source_or_dest_reg, field_get, dex_pc);
- }
-
- return true;
-}
-
-static mirror::Class* GetClassFrom(CompilerDriver* driver,
- const DexCompilationUnit& compilation_unit) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
- const DexFile& dex_file = *compilation_unit.GetDexFile();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader())));
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
-
- return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
-}
-
-mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const {
- return GetClassFrom(compiler_driver_, *outer_compilation_unit_);
-}
-
-mirror::Class* HGraphBuilder::GetCompilingClass() const {
- return GetClassFrom(compiler_driver_, *dex_compilation_unit_);
-}
-
-bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<4> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
- soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
-
- // GetOutermostCompilingClass returns null when the class is unresolved
- // (e.g. if it derives from an unresolved class). This is bogus knowing that
- // we are compiling it.
- // When this happens we cannot establish a direct relation between the current
- // class and the outer class, so we return false.
- // (Note that this is only used for optimizing invokes and field accesses)
- return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get());
-}
-
-void HGraphBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type field_type) {
- uint32_t source_or_dest_reg = instruction.VRegA_21c();
- uint16_t field_index = instruction.VRegB_21c();
-
- if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
- current_block_->AddInstruction(
- new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc));
- } else {
- current_block_->AddInstruction(
- new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
- }
-}
-bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put) {
- uint32_t source_or_dest_reg = instruction.VRegA_21c();
- uint16_t field_index = instruction.VRegB_21c();
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<5> hs(soa.Self());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(
- soa.Self(), *dex_compilation_unit_->GetDexFile())));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
- ArtField* resolved_field = compiler_driver_->ResolveField(
- soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
-
- if (resolved_field == nullptr) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index);
- BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
- return true;
- }
-
- Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
- const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
- outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
- Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
-
- // The index at which the field's class is stored in the DexCache's type array.
- uint32_t storage_index;
- bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass());
- if (is_outer_class) {
- storage_index = outer_class->GetDexTypeIndex();
- } else if (outer_dex_cache.Get() != dex_cache.Get()) {
- // The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
- return false;
- } else {
- // TODO: This is rather expensive. Perf it and cache the results if needed.
- std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
- outer_dex_cache.Get(),
- GetCompilingClass(),
- resolved_field,
- field_index,
- &storage_index);
- bool can_easily_access = is_put ? pair.second : pair.first;
- if (!can_easily_access) {
- MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess);
- BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
- return true;
- }
- }
-
- bool is_in_cache =
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index);
- HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
- storage_index,
- outer_dex_file,
- is_outer_class,
- dex_pc,
- /*needs_access_check*/ false,
- is_in_cache);
- current_block_->AddInstruction(constant);
-
- HInstruction* cls = constant;
-
- Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass()));
- if (!IsInitialized(klass)) {
- cls = new (arena_) HClinitCheck(constant, dex_pc);
- current_block_->AddInstruction(cls);
- }
-
- uint16_t class_def_index = klass->GetDexClassDefIndex();
- if (is_put) {
- // We need to keep the class alive before loading the value.
- HInstruction* value = LoadLocal(source_or_dest_reg, field_type, dex_pc);
- DCHECK_EQ(value->GetType(), field_type);
- current_block_->AddInstruction(new (arena_) HStaticFieldSet(cls,
- value,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_cache_,
- dex_pc));
- } else {
- current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls,
- field_type,
- resolved_field->GetOffset(),
- resolved_field->IsVolatile(),
- field_index,
- class_def_index,
- *dex_file_,
- dex_cache_,
- dex_pc));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
- }
- return true;
-}
-
-void HGraphBuilder::BuildCheckedDivRem(uint16_t out_vreg,
- uint16_t first_vreg,
- int64_t second_vreg_or_constant,
- uint32_t dex_pc,
- Primitive::Type type,
- bool second_is_constant,
- bool isDiv) {
- DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
-
- HInstruction* first = LoadLocal(first_vreg, type, dex_pc);
- HInstruction* second = nullptr;
- if (second_is_constant) {
- if (type == Primitive::kPrimInt) {
- second = graph_->GetIntConstant(second_vreg_or_constant, dex_pc);
- } else {
- second = graph_->GetLongConstant(second_vreg_or_constant, dex_pc);
- }
- } else {
- second = LoadLocal(second_vreg_or_constant, type, dex_pc);
- }
-
- if (!second_is_constant
- || (type == Primitive::kPrimInt && second->AsIntConstant()->GetValue() == 0)
- || (type == Primitive::kPrimLong && second->AsLongConstant()->GetValue() == 0)) {
- second = new (arena_) HDivZeroCheck(second, dex_pc);
- current_block_->AddInstruction(second);
- }
-
- if (isDiv) {
- current_block_->AddInstruction(new (arena_) HDiv(type, first, second, dex_pc));
- } else {
- current_block_->AddInstruction(new (arena_) HRem(type, first, second, dex_pc));
- }
- UpdateLocal(out_vreg, current_block_->GetLastInstruction(), dex_pc);
-}
-
-void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type anticipated_type) {
- uint8_t source_or_dest_reg = instruction.VRegA_23x();
- uint8_t array_reg = instruction.VRegB_23x();
- uint8_t index_reg = instruction.VRegC_23x();
-
- HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot, dex_pc);
- object = new (arena_) HNullCheck(object, dex_pc);
- current_block_->AddInstruction(object);
-
- HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
- current_block_->AddInstruction(length);
- HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt, dex_pc);
- index = new (arena_) HBoundsCheck(index, length, dex_pc);
- current_block_->AddInstruction(index);
- if (is_put) {
- HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type, dex_pc);
- // TODO: Insert a type check node if the type is Object.
- current_block_->AddInstruction(new (arena_) HArraySet(
- object, index, value, anticipated_type, dex_pc));
- } else {
- current_block_->AddInstruction(new (arena_) HArrayGet(object, index, anticipated_type, dex_pc));
- UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction(), dex_pc);
- }
- graph_->SetHasBoundsChecks(true);
-}
-
-void HGraphBuilder::BuildFilledNewArray(uint32_t dex_pc,
- uint32_t type_index,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index) {
- HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
- bool finalizable;
- QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
- ? kQuickAllocArrayWithAccessCheck
- : kQuickAllocArray;
- HInstruction* object = new (arena_) HNewArray(length,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- entrypoint);
- current_block_->AddInstruction(object);
-
- const char* descriptor = dex_file_->StringByTypeIdx(type_index);
- DCHECK_EQ(descriptor[0], '[') << descriptor;
- char primitive = descriptor[1];
- DCHECK(primitive == 'I'
- || primitive == 'L'
- || primitive == '[') << descriptor;
- bool is_reference_array = (primitive == 'L') || (primitive == '[');
- Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
-
- for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
- HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type, dex_pc);
- HInstruction* index = graph_->GetIntConstant(i, dex_pc);
- current_block_->AddInstruction(
- new (arena_) HArraySet(object, index, value, type, dex_pc));
- }
- latest_result_ = object;
-}
-
-template <typename T>
-void HGraphBuilder::BuildFillArrayData(HInstruction* object,
- const T* data,
- uint32_t element_count,
- Primitive::Type anticipated_type,
- uint32_t dex_pc) {
- for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = graph_->GetIntConstant(i, dex_pc);
- HInstruction* value = graph_->GetIntConstant(data[i], dex_pc);
- current_block_->AddInstruction(new (arena_) HArraySet(
- object, index, value, anticipated_type, dex_pc));
- }
-}
-
-void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
- HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot, dex_pc);
- HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
- current_block_->AddInstruction(null_check);
-
- HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
- current_block_->AddInstruction(length);
-
- int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
- const Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const Instruction::ArrayDataPayload*>(code_start_ + payload_offset);
- const uint8_t* data = payload->data;
- uint32_t element_count = payload->element_count;
-
- // Implementation of this DEX instruction seems to be that the bounds check is
- // done before doing any stores.
- HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
- current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
-
- switch (payload->element_width) {
- case 1:
- BuildFillArrayData(null_check,
- reinterpret_cast<const int8_t*>(data),
- element_count,
- Primitive::kPrimByte,
- dex_pc);
- break;
- case 2:
- BuildFillArrayData(null_check,
- reinterpret_cast<const int16_t*>(data),
- element_count,
- Primitive::kPrimShort,
- dex_pc);
- break;
- case 4:
- BuildFillArrayData(null_check,
- reinterpret_cast<const int32_t*>(data),
- element_count,
- Primitive::kPrimInt,
- dex_pc);
- break;
- case 8:
- BuildFillWideArrayData(null_check,
- reinterpret_cast<const int64_t*>(data),
- element_count,
- dex_pc);
- break;
- default:
- LOG(FATAL) << "Unknown element width for " << payload->element_width;
- }
- graph_->SetHasBoundsChecks(true);
-}
-
-void HGraphBuilder::BuildFillWideArrayData(HInstruction* object,
- const int64_t* data,
- uint32_t element_count,
- uint32_t dex_pc) {
- for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = graph_->GetIntConstant(i, dex_pc);
- HInstruction* value = graph_->GetLongConstant(data[i], dex_pc);
- current_block_->AddInstruction(new (arena_) HArraySet(
- object, index, value, Primitive::kPrimLong, dex_pc));
- }
-}
-
-static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- if (cls.Get() == nullptr) {
- return TypeCheckKind::kUnresolvedCheck;
- } else if (cls->IsInterface()) {
- return TypeCheckKind::kInterfaceCheck;
- } else if (cls->IsArrayClass()) {
- if (cls->GetComponentType()->IsObjectClass()) {
- return TypeCheckKind::kArrayObjectCheck;
- } else if (cls->CannotBeAssignedFromOtherTypes()) {
- return TypeCheckKind::kExactCheck;
- } else {
- return TypeCheckKind::kArrayCheck;
- }
- } else if (cls->IsFinal()) {
- return TypeCheckKind::kExactCheck;
- } else if (cls->IsAbstract()) {
- return TypeCheckKind::kAbstractClassCheck;
- } else {
- return TypeCheckKind::kClassHierarchyCheck;
- }
-}
-
-void HGraphBuilder::BuildTypeCheck(const Instruction& instruction,
- uint8_t destination,
- uint8_t reference,
- uint16_t type_index,
- uint32_t dex_pc) {
- bool type_known_final, type_known_abstract, use_declaring_class;
- bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(),
- *dex_compilation_unit_->GetDexFile(),
- type_index,
- &type_known_final,
- &type_known_abstract,
- &use_declaring_class);
-
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
- const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
- Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
-
- HInstruction* object = LoadLocal(reference, Primitive::kPrimNot, dex_pc);
- HLoadClass* cls = new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- type_index,
- dex_file,
- IsOutermostCompilingClass(type_index),
- dex_pc,
- !can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index));
- current_block_->AddInstruction(cls);
-
- TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
- if (instruction.Opcode() == Instruction::INSTANCE_OF) {
- current_block_->AddInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc));
- UpdateLocal(destination, current_block_->GetLastInstruction(), dex_pc);
- } else {
- DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
- // We emit a CheckCast followed by a BoundType. CheckCast is a statement
- // which may throw. If it succeeds BoundType sets the new type of `object`
- // for all subsequent uses.
- current_block_->AddInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
- current_block_->AddInstruction(new (arena_) HBoundType(object, dex_pc));
- UpdateLocal(reference, current_block_->GetLastInstruction(), dex_pc);
- }
-}
-
-bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
- return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
- 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;
-}
-
-uint16_t HGraphBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
- DCHECK(interpreter_metadata_ != nullptr);
- uint32_t dex_pc_in_map = DecodeUnsignedLeb128(&interpreter_metadata_);
- DCHECK_EQ(dex_pc, dex_pc_in_map);
- return DecodeUnsignedLeb128(&interpreter_metadata_);
-}
-
-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();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n(), dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST_16: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s(), dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i(), dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST_HIGH16: {
- int32_t register_index = instruction.VRegA();
- HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16, dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST_WIDE_16: {
- int32_t register_index = instruction.VRegA();
- // Get 16 bits of constant value, sign extended to 64 bits.
- int64_t value = instruction.VRegB_21s();
- value <<= 48;
- value >>= 48;
- HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST_WIDE_32: {
- int32_t register_index = instruction.VRegA();
- // Get 32 bits of constant value, sign extended to 64 bits.
- int64_t value = instruction.VRegB_31i();
- value <<= 32;
- value >>= 32;
- HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST_WIDE: {
- int32_t register_index = instruction.VRegA();
- HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l(), dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- case Instruction::CONST_WIDE_HIGH16: {
- int32_t register_index = instruction.VRegA();
- int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
- HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
- UpdateLocal(register_index, constant, dex_pc);
- break;
- }
-
- // Note that the SSA building will refine the types.
- case Instruction::MOVE:
- case Instruction::MOVE_FROM16:
- case Instruction::MOVE_16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
- UpdateLocal(instruction.VRegA(), value, dex_pc);
- break;
- }
-
- // Note that the SSA building will refine the types.
- case Instruction::MOVE_WIDE:
- case Instruction::MOVE_WIDE_FROM16:
- case Instruction::MOVE_WIDE_16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong, dex_pc);
- UpdateLocal(instruction.VRegA(), value, dex_pc);
- break;
- }
-
- case Instruction::MOVE_OBJECT:
- case Instruction::MOVE_OBJECT_16:
- case Instruction::MOVE_OBJECT_FROM16: {
- HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot, dex_pc);
- UpdateLocal(instruction.VRegA(), value, dex_pc);
- break;
- }
-
- case Instruction::RETURN_VOID_NO_BARRIER:
- case Instruction::RETURN_VOID: {
- BuildReturn(instruction, Primitive::kPrimVoid, dex_pc);
- break;
- }
-
-#define IF_XX(comparison, cond) \
- case Instruction::IF_##cond: If_22t<comparison>(instruction, dex_pc); break; \
- case Instruction::IF_##cond##Z: If_21t<comparison>(instruction, dex_pc); break
-
- IF_XX(HEqual, EQ);
- IF_XX(HNotEqual, NE);
- IF_XX(HLessThan, LT);
- IF_XX(HLessThanOrEqual, LE);
- IF_XX(HGreaterThan, GT);
- IF_XX(HGreaterThanOrEqual, GE);
-
- 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;
- }
-
- case Instruction::RETURN: {
- BuildReturn(instruction, return_type_, dex_pc);
- break;
- }
-
- case Instruction::RETURN_OBJECT: {
- BuildReturn(instruction, return_type_, dex_pc);
- break;
- }
-
- case Instruction::RETURN_WIDE: {
- BuildReturn(instruction, return_type_, dex_pc);
- break;
- }
-
- case Instruction::INVOKE_DIRECT:
- case Instruction::INVOKE_INTERFACE:
- case Instruction::INVOKE_STATIC:
- case Instruction::INVOKE_SUPER:
- case Instruction::INVOKE_VIRTUAL:
- case Instruction::INVOKE_VIRTUAL_QUICK: {
- uint16_t method_idx;
- if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) {
- if (!CanDecodeQuickenedInfo()) {
- return false;
- }
- method_idx = LookupQuickenedInfo(dex_pc);
- } else {
- method_idx = instruction.VRegB_35c();
- }
- uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
- uint32_t args[5];
- instruction.GetVarArgs(args);
- if (!BuildInvoke(instruction, dex_pc, method_idx,
- number_of_vreg_arguments, false, args, -1)) {
- return false;
- }
- break;
- }
-
- case Instruction::INVOKE_DIRECT_RANGE:
- case Instruction::INVOKE_INTERFACE_RANGE:
- case Instruction::INVOKE_STATIC_RANGE:
- case Instruction::INVOKE_SUPER_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE:
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
- uint16_t method_idx;
- if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) {
- if (!CanDecodeQuickenedInfo()) {
- return false;
- }
- method_idx = LookupQuickenedInfo(dex_pc);
- } else {
- method_idx = instruction.VRegB_3rc();
- }
- uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
- uint32_t register_index = instruction.VRegC();
- if (!BuildInvoke(instruction, dex_pc, method_idx,
- number_of_vreg_arguments, true, nullptr, register_index)) {
- return false;
- }
- break;
- }
-
- case Instruction::NEG_INT: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::NEG_LONG: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::NEG_FLOAT: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::NEG_DOUBLE: {
- Unop_12x<HNeg>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::NOT_INT: {
- Unop_12x<HNot>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::NOT_LONG: {
- Unop_12x<HNot>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::LONG_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::LONG_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::LONG_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::FLOAT_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::FLOAT_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::FLOAT_TO_DOUBLE: {
- Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::DOUBLE_TO_INT: {
- Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::DOUBLE_TO_LONG: {
- Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::DOUBLE_TO_FLOAT: {
- Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_BYTE: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_SHORT: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort, dex_pc);
- break;
- }
-
- case Instruction::INT_TO_CHAR: {
- Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::ADD_LONG: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_DOUBLE: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::ADD_FLOAT: {
- Binop_23x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_INT: {
- Binop_23x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SUB_LONG: {
- Binop_23x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SUB_FLOAT: {
- Binop_23x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_DOUBLE: {
- Binop_23x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT: {
- Binop_23x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::MUL_LONG: {
- Binop_23x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::MUL_FLOAT: {
- Binop_23x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::MUL_DOUBLE: {
- Binop_23x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::DIV_INT: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, false, true);
- break;
- }
-
- case Instruction::DIV_LONG: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimLong, false, true);
- break;
- }
-
- case Instruction::DIV_FLOAT: {
- Binop_23x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::DIV_DOUBLE: {
- Binop_23x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::REM_INT: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, false, false);
- break;
- }
-
- case Instruction::REM_LONG: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimLong, false, false);
- break;
- }
-
- case Instruction::REM_FLOAT: {
- Binop_23x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::REM_DOUBLE: {
- Binop_23x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::AND_INT: {
- Binop_23x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::AND_LONG: {
- Binop_23x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SHL_INT: {
- Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHL_LONG: {
- Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SHR_INT: {
- Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHR_LONG: {
- Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::USHR_INT: {
- Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::USHR_LONG: {
- Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::OR_INT: {
- Binop_23x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::OR_LONG: {
- Binop_23x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT: {
- Binop_23x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::XOR_LONG: {
- Binop_23x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_LONG_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_DOUBLE_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::ADD_FLOAT_2ADDR: {
- Binop_12x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_INT_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SUB_LONG_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SUB_FLOAT_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::SUB_DOUBLE_2ADDR: {
- Binop_12x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::MUL_LONG_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::MUL_FLOAT_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::MUL_DOUBLE_2ADDR: {
- Binop_12x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::DIV_INT_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimInt, false, true);
- break;
- }
-
- case Instruction::DIV_LONG_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimLong, false, true);
- break;
- }
-
- case Instruction::REM_INT_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimInt, false, false);
- break;
- }
-
- case Instruction::REM_LONG_2ADDR: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
- dex_pc, Primitive::kPrimLong, false, false);
- break;
- }
-
- case Instruction::REM_FLOAT_2ADDR: {
- Binop_12x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::REM_DOUBLE_2ADDR: {
- Binop_12x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::SHL_INT_2ADDR: {
- Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHL_LONG_2ADDR: {
- Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::SHR_INT_2ADDR: {
- Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::SHR_LONG_2ADDR: {
- Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::USHR_INT_2ADDR: {
- Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::USHR_LONG_2ADDR: {
- Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::DIV_FLOAT_2ADDR: {
- Binop_12x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
- break;
- }
-
- case Instruction::DIV_DOUBLE_2ADDR: {
- Binop_12x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
- break;
- }
-
- case Instruction::AND_INT_2ADDR: {
- Binop_12x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::AND_LONG_2ADDR: {
- Binop_12x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::OR_INT_2ADDR: {
- Binop_12x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::OR_LONG_2ADDR: {
- Binop_12x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT_2ADDR: {
- Binop_12x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
- break;
- }
-
- case Instruction::XOR_LONG_2ADDR: {
- Binop_12x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT_LIT16: {
- Binop_22s<HAdd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::AND_INT_LIT16: {
- Binop_22s<HAnd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::OR_INT_LIT16: {
- Binop_22s<HOr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT_LIT16: {
- Binop_22s<HXor>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::RSUB_INT: {
- Binop_22s<HSub>(instruction, true, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT_LIT16: {
- Binop_22s<HMul>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::ADD_INT_LIT8: {
- Binop_22b<HAdd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::AND_INT_LIT8: {
- Binop_22b<HAnd>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::OR_INT_LIT8: {
- Binop_22b<HOr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::XOR_INT_LIT8: {
- Binop_22b<HXor>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::RSUB_INT_LIT8: {
- Binop_22b<HSub>(instruction, true, dex_pc);
- break;
- }
-
- case Instruction::MUL_INT_LIT8: {
- Binop_22b<HMul>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::DIV_INT_LIT16:
- case Instruction::DIV_INT_LIT8: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, true, true);
- break;
- }
-
- case Instruction::REM_INT_LIT16:
- case Instruction::REM_INT_LIT8: {
- BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
- dex_pc, Primitive::kPrimInt, true, false);
- break;
- }
-
- case Instruction::SHL_INT_LIT8: {
- Binop_22b<HShl>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::SHR_INT_LIT8: {
- Binop_22b<HShr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::USHR_INT_LIT8: {
- Binop_22b<HUShr>(instruction, false, dex_pc);
- break;
- }
-
- case Instruction::NEW_INSTANCE: {
- if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) {
- return false;
- }
- UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction(), dex_pc);
- break;
- }
-
- case Instruction::NEW_ARRAY: {
- uint16_t type_index = instruction.VRegC_22c();
- HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt, dex_pc);
- bool finalizable;
- QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
- ? kQuickAllocArrayWithAccessCheck
- : kQuickAllocArray;
- current_block_->AddInstruction(new (arena_) HNewArray(length,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- entrypoint));
- UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction(), dex_pc);
- break;
- }
-
- case Instruction::FILLED_NEW_ARRAY: {
- uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
- uint32_t type_index = instruction.VRegB_35c();
- uint32_t args[5];
- instruction.GetVarArgs(args);
- BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0);
- break;
- }
-
- case Instruction::FILLED_NEW_ARRAY_RANGE: {
- uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
- uint32_t type_index = instruction.VRegB_3rc();
- uint32_t register_index = instruction.VRegC_3rc();
- BuildFilledNewArray(
- dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index);
- break;
- }
-
- case Instruction::FILL_ARRAY_DATA: {
- BuildFillArrayData(instruction, dex_pc);
- break;
- }
-
- case Instruction::MOVE_RESULT:
- case Instruction::MOVE_RESULT_WIDE:
- 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. For Invoke, we insert a StoreLocal after the instruction. For
- // FilledNewArray, the local needs to be updated after the array was
- // filled, otherwise we might overwrite an input vreg.
- HStoreLocal* update_local =
- new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_, dex_pc);
- HBasicBlock* block = latest_result_->GetBlock();
- if (block == current_block_) {
- // MoveResult and the previous instruction are in the same block.
- current_block_->AddInstruction(update_local);
- } else {
- // The two instructions are in different blocks. Insert the MoveResult
- // before the final control-flow instruction of the previous block.
- DCHECK(block->EndsWithControlFlowInstruction());
- DCHECK(current_block_->GetInstructions().IsEmpty());
- block->InsertInstructionBefore(update_local, block->GetLastInstruction());
- }
- latest_result_ = nullptr;
- }
- break;
- }
-
- case Instruction::CMP_LONG: {
- Binop_23x_cmp(instruction, Primitive::kPrimLong, ComparisonBias::kNoBias, dex_pc);
- break;
- }
-
- case Instruction::CMPG_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kGtBias, dex_pc);
- break;
- }
-
- case Instruction::CMPG_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kGtBias, dex_pc);
- break;
- }
-
- case Instruction::CMPL_FLOAT: {
- Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kLtBias, dex_pc);
- break;
- }
-
- case Instruction::CMPL_DOUBLE: {
- Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kLtBias, dex_pc);
- break;
- }
-
- case Instruction::NOP:
- break;
-
- case Instruction::IGET:
- case Instruction::IGET_QUICK:
- case Instruction::IGET_WIDE:
- case Instruction::IGET_WIDE_QUICK:
- case Instruction::IGET_OBJECT:
- case Instruction::IGET_OBJECT_QUICK:
- case Instruction::IGET_BOOLEAN:
- case Instruction::IGET_BOOLEAN_QUICK:
- case Instruction::IGET_BYTE:
- case Instruction::IGET_BYTE_QUICK:
- case Instruction::IGET_CHAR:
- case Instruction::IGET_CHAR_QUICK:
- case Instruction::IGET_SHORT:
- case Instruction::IGET_SHORT_QUICK: {
- if (!BuildInstanceFieldAccess(instruction, dex_pc, false)) {
- return false;
- }
- break;
- }
-
- case Instruction::IPUT:
- case Instruction::IPUT_QUICK:
- case Instruction::IPUT_WIDE:
- case Instruction::IPUT_WIDE_QUICK:
- case Instruction::IPUT_OBJECT:
- case Instruction::IPUT_OBJECT_QUICK:
- case Instruction::IPUT_BOOLEAN:
- case Instruction::IPUT_BOOLEAN_QUICK:
- case Instruction::IPUT_BYTE:
- case Instruction::IPUT_BYTE_QUICK:
- case Instruction::IPUT_CHAR:
- case Instruction::IPUT_CHAR_QUICK:
- case Instruction::IPUT_SHORT:
- case Instruction::IPUT_SHORT_QUICK: {
- if (!BuildInstanceFieldAccess(instruction, dex_pc, true)) {
- return false;
- }
- break;
- }
-
- case Instruction::SGET:
- case Instruction::SGET_WIDE:
- case Instruction::SGET_OBJECT:
- case Instruction::SGET_BOOLEAN:
- case Instruction::SGET_BYTE:
- case Instruction::SGET_CHAR:
- case Instruction::SGET_SHORT: {
- if (!BuildStaticFieldAccess(instruction, dex_pc, false)) {
- return false;
- }
- break;
- }
-
- case Instruction::SPUT:
- case Instruction::SPUT_WIDE:
- case Instruction::SPUT_OBJECT:
- case Instruction::SPUT_BOOLEAN:
- case Instruction::SPUT_BYTE:
- case Instruction::SPUT_CHAR:
- case Instruction::SPUT_SHORT: {
- if (!BuildStaticFieldAccess(instruction, dex_pc, true)) {
- return false;
- }
- break;
- }
-
-#define ARRAY_XX(kind, anticipated_type) \
- case Instruction::AGET##kind: { \
- BuildArrayAccess(instruction, dex_pc, false, anticipated_type); \
- break; \
- } \
- case Instruction::APUT##kind: { \
- BuildArrayAccess(instruction, dex_pc, true, anticipated_type); \
- break; \
- }
-
- ARRAY_XX(, Primitive::kPrimInt);
- ARRAY_XX(_WIDE, Primitive::kPrimLong);
- ARRAY_XX(_OBJECT, Primitive::kPrimNot);
- ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean);
- ARRAY_XX(_BYTE, Primitive::kPrimByte);
- ARRAY_XX(_CHAR, Primitive::kPrimChar);
- ARRAY_XX(_SHORT, Primitive::kPrimShort);
-
- case Instruction::ARRAY_LENGTH: {
- HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot, dex_pc);
- object = new (arena_) HNullCheck(object, dex_pc);
- current_block_->AddInstruction(object);
- current_block_->AddInstruction(new (arena_) HArrayLength(object, dex_pc));
- UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction(), dex_pc);
- break;
- }
-
- case Instruction::CONST_STRING: {
- uint32_t string_index = instruction.VRegB_21c();
- current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
- UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
- break;
- }
-
- case Instruction::CONST_STRING_JUMBO: {
- uint32_t string_index = instruction.VRegB_31c();
- current_block_->AddInstruction(
- new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
- UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction(), dex_pc);
- break;
- }
-
- case Instruction::CONST_CLASS: {
- uint16_t type_index = instruction.VRegB_21c();
- bool type_known_final;
- bool type_known_abstract;
- bool dont_use_is_referrers_class;
- // `CanAccessTypeWithoutChecks` will tell whether the method being
- // built is trying to access its own class, so that the generated
- // code can optimize for this case. However, the optimization does not
- // work for inlining, so we use `IsOutermostCompilingClass` instead.
- bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
- &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
- current_block_->AddInstruction(new (arena_) HLoadClass(
- graph_->GetCurrentMethod(),
- type_index,
- *dex_file_,
- IsOutermostCompilingClass(type_index),
- dex_pc,
- !can_access,
- compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index)));
- UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction(), dex_pc);
- break;
- }
-
- case Instruction::MOVE_EXCEPTION: {
- current_block_->AddInstruction(new (arena_) HLoadException(dex_pc));
- UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction(), dex_pc);
- current_block_->AddInstruction(new (arena_) HClearException(dex_pc));
- break;
- }
-
- 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;
- break;
- }
-
- case Instruction::INSTANCE_OF: {
- uint8_t destination = instruction.VRegA_22c();
- uint8_t reference = instruction.VRegB_22c();
- uint16_t type_index = instruction.VRegC_22c();
- BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
- break;
- }
-
- case Instruction::CHECK_CAST: {
- uint8_t reference = instruction.VRegA_21c();
- uint16_t type_index = instruction.VRegB_21c();
- BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
- break;
- }
-
- case Instruction::MONITOR_ENTER: {
- current_block_->AddInstruction(new (arena_) HMonitorOperation(
- LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
- HMonitorOperation::OperationKind::kEnter,
- dex_pc));
- break;
- }
-
- case Instruction::MONITOR_EXIT: {
- current_block_->AddInstruction(new (arena_) HMonitorOperation(
- LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
- HMonitorOperation::OperationKind::kExit,
- dex_pc));
- break;
- }
-
- case Instruction::PACKED_SWITCH: {
- BuildPackedSwitch(instruction, dex_pc);
- break;
- }
-
- case Instruction::SPARSE_SWITCH: {
- BuildSparseSwitch(instruction, dex_pc);
- break;
- }
-
- default:
- VLOG(compiler) << "Did not compile "
- << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << " because of unhandled instruction "
- << instruction.Name();
- MaybeRecordStat(MethodCompilationStat::kNotCompiledUnhandledInstruction);
- return false;
+ // 4) Populate basic blocks with instructions.
+ if (!instruction_builder_.Build()) {
+ return kAnalysisInvalidBytecode;
}
- return true;
-} // NOLINT(readability/fn_size)
-
-HLocal* HGraphBuilder::GetLocalAt(uint32_t register_index) const {
- return locals_[register_index];
-}
-
-void HGraphBuilder::UpdateLocal(uint32_t register_index,
- HInstruction* instruction,
- uint32_t dex_pc) const {
- HLocal* local = GetLocalAt(register_index);
- current_block_->AddInstruction(new (arena_) HStoreLocal(local, instruction, dex_pc));
-}
-HInstruction* HGraphBuilder::LoadLocal(uint32_t register_index,
- Primitive::Type type,
- uint32_t dex_pc) const {
- HLocal* local = GetLocalAt(register_index);
- current_block_->AddInstruction(new (arena_) HLoadLocal(local, type, dex_pc));
- return current_block_->GetLastInstruction();
+ // 5) Type the graph and eliminate dead/redundant phis.
+ return ssa_builder_.BuildSsa();
}
} // namespace art
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 48f5316222..4f46d5edda 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -19,320 +19,90 @@
#include "base/arena_containers.h"
#include "base/arena_object.h"
+#include "block_builder.h"
#include "dex_file.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
+#include "instruction_builder.h"
#include "optimizing_compiler_stats.h"
#include "primitive.h"
#include "nodes.h"
+#include "ssa_builder.h"
namespace art {
-class Instruction;
-
class HGraphBuilder : public ValueObject {
public:
HGraphBuilder(HGraph* graph,
DexCompilationUnit* dex_compilation_unit,
const DexCompilationUnit* const outer_compilation_unit,
const DexFile* dex_file,
+ const DexFile::CodeItem& code_item,
CompilerDriver* driver,
OptimizingCompilerStats* compiler_stats,
const uint8_t* interpreter_metadata,
- Handle<mirror::DexCache> dex_cache)
- : arena_(graph->GetArena()),
- branch_targets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
- locals_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
- entry_block_(nullptr),
- exit_block_(nullptr),
- current_block_(nullptr),
- graph_(graph),
+ Handle<mirror::DexCache> dex_cache,
+ StackHandleScopeCollection* handles)
+ : graph_(graph),
dex_file_(dex_file),
+ code_item_(code_item),
dex_compilation_unit_(dex_compilation_unit),
compiler_driver_(driver),
- outer_compilation_unit_(outer_compilation_unit),
- return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
- code_start_(nullptr),
- latest_result_(nullptr),
compilation_stats_(compiler_stats),
- interpreter_metadata_(interpreter_metadata),
- dex_cache_(dex_cache) {}
+ block_builder_(graph, dex_file, code_item),
+ ssa_builder_(graph, handles),
+ instruction_builder_(graph,
+ &block_builder_,
+ &ssa_builder_,
+ dex_file,
+ code_item_,
+ Primitive::GetType(dex_compilation_unit_->GetShorty()[0]),
+ dex_compilation_unit,
+ outer_compilation_unit,
+ driver,
+ interpreter_metadata,
+ compiler_stats,
+ dex_cache) {}
// Only for unit testing.
- HGraphBuilder(HGraph* graph, Primitive::Type return_type = Primitive::kPrimInt)
- : arena_(graph->GetArena()),
- branch_targets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
- locals_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
- entry_block_(nullptr),
- exit_block_(nullptr),
- current_block_(nullptr),
- graph_(graph),
+ HGraphBuilder(HGraph* graph,
+ const DexFile::CodeItem& code_item,
+ StackHandleScopeCollection* handles,
+ Primitive::Type return_type = Primitive::kPrimInt)
+ : graph_(graph),
dex_file_(nullptr),
+ code_item_(code_item),
dex_compilation_unit_(nullptr),
compiler_driver_(nullptr),
- outer_compilation_unit_(nullptr),
- return_type_(return_type),
- code_start_(nullptr),
- latest_result_(nullptr),
- compilation_stats_(nullptr),
- interpreter_metadata_(nullptr),
null_dex_cache_(),
- dex_cache_(null_dex_cache_) {}
-
- GraphAnalysisResult BuildGraph(const DexFile::CodeItem& code,
- StackHandleScopeCollection* handles);
+ compilation_stats_(nullptr),
+ block_builder_(graph, nullptr, code_item),
+ ssa_builder_(graph, handles),
+ instruction_builder_(graph,
+ &block_builder_,
+ &ssa_builder_,
+ /* dex_file */ nullptr,
+ code_item_,
+ return_type,
+ /* dex_compilation_unit */ nullptr,
+ /* outer_compilation_unit */ nullptr,
+ /* compiler_driver */ nullptr,
+ /* interpreter_metadata */ nullptr,
+ /* compiler_stats */ nullptr,
+ null_dex_cache_) {}
+
+ GraphAnalysisResult BuildGraph();
static constexpr const char* kBuilderPassName = "builder";
- // The number of entries in a packed switch before we use a jump table or specified
- // compare/jump series.
- static constexpr uint16_t kSmallSwitchThreshold = 3;
-
private:
- // Analyzes the dex instruction and adds HInstruction to the graph
- // to execute that instruction. Returns whether the instruction can
- // be handled.
- bool AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_pc);
-
- // Finds all instructions that start a new block, and populates branch_targets_ with
- // the newly created blocks.
- // As a side effect, also compute the number of dex instructions, blocks, and
- // branches.
- // Returns true if all the branches fall inside the method code, false otherwise.
- // (In normal cases this should always return true but someone can artificially
- // create a code unit in which branches fall-through out of it).
- bool ComputeBranchTargets(const uint16_t* start,
- const uint16_t* end,
- size_t* number_of_branches);
- void MaybeUpdateCurrentBlock(size_t dex_pc);
- void FindNativeDebugInfoLocations(const DexFile::CodeItem& code_item, ArenaBitVector* locations);
- HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
- HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
-
- // Adds new blocks to `branch_targets_` starting at the limits of TryItems and
- // their exception handlers.
- void CreateBlocksForTryCatch(const DexFile::CodeItem& code_item);
-
- // Splits edges which cross the boundaries of TryItems, inserts TryBoundary
- // instructions and links them to the corresponding catch blocks.
- void InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item);
-
- // Iterates over the exception handlers of `try_item`, finds the corresponding
- // catch blocks and makes them successors of `try_boundary`. The order of
- // successors matches the order in which runtime exception delivery searches
- // for a handler.
- void LinkToCatchBlocks(HTryBoundary* try_boundary,
- const DexFile::CodeItem& code_item,
- const DexFile::TryItem* try_item);
-
- bool CanDecodeQuickenedInfo() const;
- uint16_t LookupQuickenedInfo(uint32_t dex_pc);
-
- void InitializeLocals(uint16_t count);
- HLocal* GetLocalAt(uint32_t register_index) const;
- void UpdateLocal(uint32_t register_index, HInstruction* instruction, uint32_t dex_pc) const;
- HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type, uint32_t dex_pc) const;
- void InitializeParameters(uint16_t number_of_parameters);
-
- // Returns whether the current method needs access check for the type.
- // Output parameter finalizable is set to whether the type is finalizable.
- bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const;
-
- template<typename T>
- void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_23x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- void Binop_23x_cmp(const Instruction& instruction,
- Primitive::Type type,
- ComparisonBias bias,
- uint32_t dex_pc);
-
- template<typename T>
- void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_12x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- template<typename T>
- void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);
-
- template<typename T>
- void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);
-
- template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
- template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);
-
- void Conversion_12x(const Instruction& instruction,
- Primitive::Type input_type,
- Primitive::Type result_type,
- uint32_t dex_pc);
-
- void BuildCheckedDivRem(uint16_t out_reg,
- uint16_t first_reg,
- int64_t second_reg_or_constant,
- uint32_t dex_pc,
- Primitive::Type type,
- bool second_is_lit,
- bool is_div);
-
- void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
-
- // Builds an instance field access node and returns whether the instruction is supported.
- bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
-
- void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_put,
- Primitive::Type field_type);
- // Builds a static field access node and returns whether the instruction is supported.
- bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
-
- void BuildArrayAccess(const Instruction& instruction,
- uint32_t dex_pc,
- bool is_get,
- Primitive::Type anticipated_type);
-
- // Builds an invocation node and returns whether the instruction is supported.
- bool BuildInvoke(const Instruction& instruction,
- uint32_t dex_pc,
- uint32_t method_idx,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index);
-
- // Builds a new array node and the instructions that fill it.
- void BuildFilledNewArray(uint32_t dex_pc,
- uint32_t type_index,
- uint32_t number_of_vreg_arguments,
- bool is_range,
- uint32_t* args,
- uint32_t register_index);
-
- void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc);
-
- // Fills the given object with data as specified in the fill-array-data
- // instruction. Currently only used for non-reference and non-floating point
- // arrays.
- template <typename T>
- void BuildFillArrayData(HInstruction* object,
- const T* data,
- uint32_t element_count,
- Primitive::Type anticipated_type,
- uint32_t dex_pc);
-
- // Fills the given object with data as specified in the fill-array-data
- // instruction. The data must be for long and double arrays.
- void BuildFillWideArrayData(HInstruction* object,
- const int64_t* data,
- uint32_t element_count,
- uint32_t dex_pc);
-
- // Builds a `HInstanceOf`, or a `HCheckCast` instruction.
- void BuildTypeCheck(const Instruction& instruction,
- uint8_t destination,
- uint8_t reference,
- uint16_t type_index,
- uint32_t dex_pc);
-
- // Builds an instruction sequence for a packed switch statement.
- void BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc);
-
- // Build a switch instruction from a packed switch statement.
- void BuildSwitchJumpTable(const SwitchTable& table,
- const Instruction& instruction,
- HInstruction* value,
- uint32_t dex_pc);
-
- // Builds an instruction sequence for a sparse switch statement.
- void BuildSparseSwitch(const Instruction& instruction, uint32_t dex_pc);
-
- void 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);
-
- bool SkipCompilation(const DexFile::CodeItem& code_item, size_t number_of_branches);
-
void MaybeRecordStat(MethodCompilationStat compilation_stat);
+ bool SkipCompilation(size_t number_of_branches);
- // Returns the outer-most compiling method's class.
- mirror::Class* GetOutermostCompilingClass() const;
-
- // Returns the class whose method is being compiled.
- mirror::Class* GetCompilingClass() const;
-
- // Returns whether `type_index` points to the outer-most compiling method's class.
- bool IsOutermostCompilingClass(uint16_t type_index) const;
-
- void PotentiallySimplifyFakeString(uint16_t original_dex_register,
- uint32_t dex_pc,
- HInvoke* invoke);
-
- bool SetupInvokeArguments(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- size_t start_index,
- size_t* argument_index);
-
- bool HandleInvoke(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor,
- HClinitCheck* clinit_check);
-
- bool HandleStringInit(HInvoke* invoke,
- uint32_t number_of_vreg_arguments,
- uint32_t* args,
- uint32_t register_index,
- bool is_range,
- const char* descriptor);
-
- HClinitCheck* ProcessClinitCheckForInvoke(
- uint32_t dex_pc,
- ArtMethod* method,
- uint32_t method_idx,
- HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- // Build a HNewInstance instruction.
- bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc);
-
- // Return whether the compiler can assume `cls` is initialized.
- bool IsInitialized(Handle<mirror::Class> cls) const
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- // Try to resolve a method using the class linker. Return null if a method could
- // not be resolved.
- ArtMethod* ResolveMethod(uint16_t method_idx, InvokeType invoke_type);
-
- ArenaAllocator* const arena_;
-
- // A list of the size of the dex code holding block information for
- // the method. If an entry contains a block, then the dex instruction
- // starting at that entry is the first instruction of a new block.
- ArenaVector<HBasicBlock*> branch_targets_;
-
- ArenaVector<HLocal*> locals_;
-
- HBasicBlock* entry_block_;
- HBasicBlock* exit_block_;
- HBasicBlock* current_block_;
HGraph* const graph_;
-
- // The dex file where the method being compiled is.
const DexFile* const dex_file_;
+ const DexFile::CodeItem& code_item_;
// The compilation unit of the current method being compiled. Note that
// it can be an inlined method.
@@ -340,29 +110,13 @@ class HGraphBuilder : public ValueObject {
CompilerDriver* const compiler_driver_;
- // The compilation unit of the outermost method being compiled. That is the
- // method being compiled (and not inlined), and potentially inlining other
- // methods.
- const DexCompilationUnit* const outer_compilation_unit_;
-
- // The return type of the method being compiled.
- const Primitive::Type return_type_;
-
- // The pointer in the dex file where the instructions of the code item
- // being currently compiled start.
- const uint16_t* code_start_;
-
- // The last invoke or fill-new-array being built. Only to be
- // used by move-result instructions.
- HInstruction* latest_result_;
+ ScopedNullHandle<mirror::DexCache> null_dex_cache_;
OptimizingCompilerStats* compilation_stats_;
- const uint8_t* interpreter_metadata_;
-
- // Dex cache for dex_file_.
- ScopedNullHandle<mirror::DexCache> null_dex_cache_;
- Handle<mirror::DexCache> dex_cache_;
+ HBasicBlockBuilder block_builder_;
+ SsaBuilder ssa_builder_;
+ HInstructionBuilder instruction_builder_;
DISALLOW_COPY_AND_ASSIGN(HGraphBuilder);
};
diff --git a/compiler/optimizing/bytecode_utils.h b/compiler/optimizing/bytecode_utils.h
new file mode 100644
index 0000000000..6dfffce117
--- /dev/null
+++ b/compiler/optimizing/bytecode_utils.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_
+#define ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_
+
+#include "base/arena_object.h"
+#include "dex_file.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+
+namespace art {
+
+class CodeItemIterator : public ValueObject {
+ public:
+ CodeItemIterator(const DexFile::CodeItem& code_item, uint32_t start_dex_pc = 0u)
+ : code_ptr_(code_item.insns_ + start_dex_pc),
+ code_end_(code_item.insns_ + code_item.insns_size_in_code_units_),
+ dex_pc_(start_dex_pc) {}
+
+ bool Done() const { return code_ptr_ >= code_end_; }
+ bool IsLast() const { return code_ptr_ + CurrentInstruction().SizeInCodeUnits() >= code_end_; }
+
+ const Instruction& CurrentInstruction() const { return *Instruction::At(code_ptr_); }
+ uint32_t CurrentDexPc() const { return dex_pc_; }
+
+ void Advance() {
+ DCHECK(!Done());
+ size_t instruction_size = CurrentInstruction().SizeInCodeUnits();
+ code_ptr_ += instruction_size;
+ dex_pc_ += instruction_size;
+ }
+
+ private:
+ const uint16_t* code_ptr_;
+ const uint16_t* const code_end_;
+ uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeItemIterator);
+};
+
+class DexSwitchTable : public ValueObject {
+ public:
+ DexSwitchTable(const Instruction& instruction, uint32_t dex_pc)
+ : instruction_(instruction),
+ dex_pc_(dex_pc),
+ sparse_(instruction.Opcode() == Instruction::SPARSE_SWITCH) {
+ int32_t table_offset = instruction.VRegB_31t();
+ const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset;
+ DCHECK_EQ(table[0], sparse_ ? static_cast<uint16_t>(Instruction::kSparseSwitchSignature)
+ : static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
+ num_entries_ = table[1];
+ values_ = reinterpret_cast<const int32_t*>(&table[2]);
+ }
+
+ uint16_t GetNumEntries() const {
+ return num_entries_;
+ }
+
+ void CheckIndex(size_t index) const {
+ if (sparse_) {
+ // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+ DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_));
+ } else {
+ // In a packed table, we have the starting key and num_entries_ values.
+ DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_));
+ }
+ }
+
+ int32_t GetEntryAt(size_t index) const {
+ CheckIndex(index);
+ return values_[index];
+ }
+
+ uint32_t GetDexPcForIndex(size_t index) const {
+ CheckIndex(index);
+ return dex_pc_ +
+ (reinterpret_cast<const int16_t*>(values_ + index) -
+ reinterpret_cast<const int16_t*>(&instruction_));
+ }
+
+ // Index of the first value in the table.
+ size_t GetFirstValueIndex() const {
+ if (sparse_) {
+ // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
+ return num_entries_;
+ } else {
+ // In a packed table, we have the starting key and num_entries_ values.
+ return 1;
+ }
+ }
+
+ bool IsSparse() const { return sparse_; }
+
+ bool ShouldBuildDecisionTree() {
+ return IsSparse() || GetNumEntries() <= kSmallSwitchThreshold;
+ }
+
+ private:
+ const Instruction& instruction_;
+ const uint32_t dex_pc_;
+
+ // Whether this is a sparse-switch table (or a packed-switch one).
+ const bool sparse_;
+
+ // This can't be const as it needs to be computed off of the given instruction, and complicated
+ // expressions in the initializer list seemed very ugly.
+ uint16_t num_entries_;
+
+ const int32_t* values_;
+
+ // The number of entries in a packed switch before we use a jump table or specified
+ // compare/jump series.
+ static constexpr uint16_t kSmallSwitchThreshold = 3;
+
+ DISALLOW_COPY_AND_ASSIGN(DexSwitchTable);
+};
+
+class DexSwitchTableIterator {
+ public:
+ explicit DexSwitchTableIterator(const DexSwitchTable& table)
+ : table_(table),
+ num_entries_(static_cast<size_t>(table_.GetNumEntries())),
+ first_target_offset_(table_.GetFirstValueIndex()),
+ index_(0u) {}
+
+ bool Done() const { return index_ >= num_entries_; }
+ bool IsLast() const { return index_ == num_entries_ - 1; }
+
+ void Advance() {
+ DCHECK(!Done());
+ index_++;
+ }
+
+ int32_t CurrentKey() const {
+ return table_.IsSparse() ? table_.GetEntryAt(index_) : table_.GetEntryAt(0) + index_;
+ }
+
+ int32_t CurrentTargetOffset() const {
+ return table_.GetEntryAt(index_ + first_target_offset_);
+ }
+
+ uint32_t GetDexPcForCurrentIndex() const { return table_.GetDexPcForIndex(index_); }
+
+ private:
+ const DexSwitchTable& table_;
+ const size_t num_entries_;
+ const size_t first_target_offset_;
+
+ size_t index_;
+};
+
+inline const Instruction& GetDexInstructionAt(const DexFile::CodeItem& code_item, uint32_t dex_pc) {
+ return CodeItemIterator(code_item, dex_pc).CurrentInstruction();
+}
+
+inline bool IsThrowingDexInstruction(const Instruction& instruction) {
+ // Special-case MONITOR_EXIT which is a throwing instruction but the verifier
+ // guarantees that it will never throw. This is necessary to avoid rejecting
+ // 'synchronized' blocks/methods.
+ return instruction.IsThrow() && instruction.Opcode() != Instruction::MONITOR_EXIT;
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 7cf9072598..65e5c3ad48 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -40,6 +40,7 @@
#include "code_generator_mips64.h"
#endif
+#include "bytecode_utils.h"
#include "compiled_method.h"
#include "dex/verified_method.h"
#include "driver/compiler_driver.h"
@@ -298,23 +299,6 @@ void CodeGenerator::InitializeCodeGeneration(size_t number_of_spill_slots,
}
}
-int32_t CodeGenerator::GetStackSlot(HLocal* local) const {
- uint16_t reg_number = local->GetRegNumber();
- uint16_t number_of_locals = GetGraph()->GetNumberOfLocalVRegs();
- if (reg_number >= number_of_locals) {
- // Local is a parameter of the method. It is stored in the caller's frame.
- // TODO: Share this logic with StackVisitor::GetVRegOffsetFromQuickCode.
- return GetFrameSize() + InstructionSetPointerSize(GetInstructionSet()) // ART method
- + (reg_number - number_of_locals) * kVRegSize;
- } else {
- // Local is a temporary in this method. It is stored in this method's frame.
- return GetFrameSize() - FrameEntrySpillSize()
- - kVRegSize // filler.
- - (number_of_locals * kVRegSize)
- + (reg_number * kVRegSize);
- }
-}
-
void CodeGenerator::CreateCommonInvokeLocationSummary(
HInvoke* invoke, InvokeDexCallingConventionVisitor* visitor) {
ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena();
@@ -680,7 +664,7 @@ static void CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph,
uint32_t target = dex_pc + instruction.GetTargetOffset();
CheckCovers(target, graph, code_info, loop_headers, &covered);
} else if (instruction.IsSwitch()) {
- SwitchTable table(instruction, dex_pc, instruction.Opcode() == Instruction::SPARSE_SWITCH);
+ DexSwitchTable table(instruction, dex_pc);
uint16_t num_entries = table.GetNumEntries();
size_t offset = table.GetFirstValueIndex();
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index cad55296bc..1a060b1f58 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -211,7 +211,6 @@ class CodeGenerator {
size_t maximum_number_of_live_fpu_registers,
size_t number_of_out_slots,
const ArenaVector<HBasicBlock*>& block_order);
- int32_t GetStackSlot(HLocal* local) const;
uint32_t GetFrameSize() const { return frame_size_; }
void SetFrameSize(uint32_t size) { frame_size_ = size; }
@@ -525,8 +524,6 @@ class CodeGenerator {
slow_paths_.reserve(8);
}
- virtual Location GetStackLocation(HLoadLocal* load) const = 0;
-
virtual HGraphVisitor* GetLocationBuilder() = 0;
virtual HGraphVisitor* GetInstructionVisitor() = 0;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 98577d67ea..a0c14123f3 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -954,30 +954,6 @@ void CodeGeneratorARM::Bind(HBasicBlock* block) {
__ BindTrackedLabel(label);
}
-Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
- switch (load->GetType()) {
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unexpected type " << load->GetType();
- UNREACHABLE();
- }
-
- LOG(FATAL) << "Unreachable";
- UNREACHABLE();
-}
-
Location InvokeDexCallingConventionVisitorARM::GetNextLocation(Primitive::Type type) {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1724,49 +1700,6 @@ void InstructionCodeGeneratorARM::VisitAboveOrEqual(HAboveOrEqual* comp) {
HandleCondition(comp);
}
-void LocationsBuilderARM::VisitLocal(HLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitLocal(HLocal* local) {
- DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-}
-
-void LocationsBuilderARM::VisitLoadLocal(HLoadLocal* load) {
- load->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
-void LocationsBuilderARM::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
- switch (store->InputAt(1)->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType();
- }
-}
-
-void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
-}
-
void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 84341284a3..144d58d85a 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -345,8 +345,6 @@ class CodeGeneratorARM : public CodeGenerator {
void SetupBlockedRegisters() const OVERRIDE;
- Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 491014d3e5..7699ddd761 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1072,31 +1072,6 @@ void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* l
}
}
-Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const {
- Primitive::Type type = load->GetType();
-
- switch (type) {
- case Primitive::kPrimNot:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unexpected type " << type;
- }
-
- LOG(FATAL) << "Unreachable";
- return Location::NoLocation();
-}
-
void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register card = temps.AcquireX();
@@ -4010,14 +3985,6 @@ void InstructionCodeGeneratorARM64::VisitClearException(HClearException* clear A
__ Str(wzr, GetExceptionTlsAddress());
}
-void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) {
- load->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
HLoadString::LoadKind desired_string_load_kind) {
if (kEmitCompilerReadBarrier) {
@@ -4156,14 +4123,6 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) {
}
}
-void LocationsBuilderARM64::VisitLocal(HLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM64::VisitLocal(HLocal* local) {
- DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-}
-
void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
locations->SetOut(Location::ConstantLocation(constant));
@@ -4556,34 +4515,6 @@ void InstructionCodeGeneratorARM64::VisitShr(HShr* shr) {
HandleShift(shr);
}
-void LocationsBuilderARM64::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
- Primitive::Type field_type = store->InputAt(1)->GetType();
- switch (field_type) {
- case Primitive::kPrimNot:
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unimplemented local type " << field_type;
- UNREACHABLE();
- }
-}
-
-void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
-}
-
void LocationsBuilderARM64::VisitSub(HSub* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 8ec753159a..ec46a34615 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -386,8 +386,6 @@ class CodeGeneratorARM64 : public CodeGenerator {
void SetupBlockedRegisters() const OVERRIDE;
- Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8b19f84e1c..2df37cd429 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -974,31 +974,6 @@ void CodeGeneratorMIPS::AddLocationAsTemp(Location location, LocationSummary* lo
}
}
-Location CodeGeneratorMIPS::GetStackLocation(HLoadLocal* load) const {
- Primitive::Type type = load->GetType();
-
- switch (type) {
- case Primitive::kPrimNot:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unexpected type " << type;
- }
-
- LOG(FATAL) << "Unreachable";
- return Location::NoLocation();
-}
-
void CodeGeneratorMIPS::MarkGCCard(Register object, Register value) {
MipsLabel done;
Register card = AT;
@@ -4063,14 +4038,6 @@ void InstructionCodeGeneratorMIPS::VisitClearException(HClearException* clear AT
__ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
}
-void LocationsBuilderMIPS::VisitLoadLocal(HLoadLocal* load) {
- load->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
LocationSummary::CallKind call_kind = load->NeedsEnvironment()
? LocationSummary::kCallOnSlowPath
@@ -4096,14 +4063,6 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) {
}
}
-void LocationsBuilderMIPS::VisitLocal(HLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS::VisitLocal(HLocal* local) {
- DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-}
-
void LocationsBuilderMIPS::VisitLongConstant(HLongConstant* constant) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
locations->SetOut(Location::ConstantLocation(constant));
@@ -4611,33 +4570,6 @@ void InstructionCodeGeneratorMIPS::VisitShr(HShr* shr) {
HandleShift(shr);
}
-void LocationsBuilderMIPS::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
- Primitive::Type field_type = store->InputAt(1)->GetType();
- switch (field_type) {
- case Primitive::kPrimNot:
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unimplemented local type " << field_type;
- }
-}
-
-void InstructionCodeGeneratorMIPS::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
-}
-
void LocationsBuilderMIPS::VisitSub(HSub* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index afe7917cc4..5e6fec8cf5 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -290,8 +290,6 @@ class CodeGeneratorMIPS : public CodeGenerator {
void SetupBlockedRegisters() const OVERRIDE;
- Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 2f9eca6ac3..cc1f372898 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -869,31 +869,6 @@ void CodeGeneratorMIPS64::AddLocationAsTemp(Location location, LocationSummary*
}
}
-Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const {
- Primitive::Type type = load->GetType();
-
- switch (type) {
- case Primitive::kPrimNot:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unexpected type " << type;
- }
-
- LOG(FATAL) << "Unreachable";
- return Location::NoLocation();
-}
-
void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object,
GpuRegister value,
bool value_can_be_null) {
@@ -3281,14 +3256,6 @@ void InstructionCodeGeneratorMIPS64::VisitClearException(HClearException* clear
__ StoreToOffset(kStoreWord, ZERO, TR, GetExceptionTlsOffset());
}
-void LocationsBuilderMIPS64::VisitLoadLocal(HLoadLocal* load) {
- load->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
LocationSummary::CallKind call_kind = load->NeedsEnvironment()
? LocationSummary::kCallOnSlowPath
@@ -3317,14 +3284,6 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
}
}
-void LocationsBuilderMIPS64::VisitLocal(HLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS64::VisitLocal(HLocal* local) {
- DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-}
-
void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
locations->SetOut(Location::ConstantLocation(constant));
@@ -3745,33 +3704,6 @@ void InstructionCodeGeneratorMIPS64::VisitShr(HShr* shr) {
HandleShift(shr);
}
-void LocationsBuilderMIPS64::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(store);
- Primitive::Type field_type = store->InputAt(1)->GetType();
- switch (field_type) {
- case Primitive::kPrimNot:
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unimplemented local type " << field_type;
- }
-}
-
-void InstructionCodeGeneratorMIPS64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
-}
-
void LocationsBuilderMIPS64::VisitSub(HSub* instruction) {
HandleBinaryOp(instruction);
}
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 94767cba4b..4e15cdd7b5 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -286,8 +286,6 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
void SetupBlockedRegisters() const OVERRIDE;
- Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id);
size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id);
size_t SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 715b5be2c8..94d2f0c0a5 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -892,30 +892,6 @@ void CodeGeneratorX86::Bind(HBasicBlock* block) {
__ Bind(GetLabelOf(block));
}
-Location CodeGeneratorX86::GetStackLocation(HLoadLocal* load) const {
- switch (load->GetType()) {
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unexpected type " << load->GetType();
- UNREACHABLE();
- }
-
- LOG(FATAL) << "Unreachable";
- UNREACHABLE();
-}
-
Location InvokeDexCallingConventionVisitorX86::GetReturnLocation(Primitive::Type type) const {
switch (type) {
case Primitive::kPrimBoolean:
@@ -1646,49 +1622,6 @@ void CodeGeneratorX86::GenerateNop() {
__ nop();
}
-void LocationsBuilderX86::VisitLocal(HLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86::VisitLocal(HLocal* local) {
- DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-}
-
-void LocationsBuilderX86::VisitLoadLocal(HLoadLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
-void LocationsBuilderX86::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
- switch (store->InputAt(1)->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unknown local type " << store->InputAt(1)->GetType();
- }
-}
-
-void InstructionCodeGeneratorX86::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
-}
-
void LocationsBuilderX86::HandleCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 1fa22fcfbe..69a625306f 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -367,8 +367,6 @@ class CodeGeneratorX86 : public CodeGenerator {
void SetupBlockedRegisters() const OVERRIDE;
- Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index cc46a07dcb..da126e4b57 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1118,30 +1118,6 @@ void CodeGeneratorX86_64::Bind(HBasicBlock* block) {
__ Bind(GetLabelOf(block));
}
-Location CodeGeneratorX86_64::GetStackLocation(HLoadLocal* load) const {
- switch (load->GetType()) {
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- return Location::DoubleStackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- return Location::StackSlot(GetStackSlot(load->GetLocal()));
-
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimVoid:
- LOG(FATAL) << "Unexpected type " << load->GetType();
- UNREACHABLE();
- }
-
- LOG(FATAL) << "Unreachable";
- UNREACHABLE();
-}
-
void CodeGeneratorX86_64::Move(Location destination, Location source) {
if (source.Equals(destination)) {
return;
@@ -1660,49 +1636,6 @@ void CodeGeneratorX86_64::GenerateNop() {
__ nop();
}
-void LocationsBuilderX86_64::VisitLocal(HLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86_64::VisitLocal(HLocal* local) {
- DCHECK_EQ(local->GetBlock(), GetGraph()->GetEntryBlock());
-}
-
-void LocationsBuilderX86_64::VisitLoadLocal(HLoadLocal* local) {
- local->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86_64::VisitLoadLocal(HLoadLocal* load ATTRIBUTE_UNUSED) {
- // Nothing to do, this is driven by the code generator.
-}
-
-void LocationsBuilderX86_64::VisitStoreLocal(HStoreLocal* store) {
- LocationSummary* locations =
- new (GetGraph()->GetArena()) LocationSummary(store, LocationSummary::kNoCall);
- switch (store->InputAt(1)->GetType()) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimChar:
- case Primitive::kPrimShort:
- case Primitive::kPrimInt:
- case Primitive::kPrimNot:
- case Primitive::kPrimFloat:
- locations->SetInAt(1, Location::StackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- case Primitive::kPrimLong:
- case Primitive::kPrimDouble:
- locations->SetInAt(1, Location::DoubleStackSlot(codegen_->GetStackSlot(store->GetLocal())));
- break;
-
- default:
- LOG(FATAL) << "Unexpected local type " << store->InputAt(1)->GetType();
- }
-}
-
-void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store ATTRIBUTE_UNUSED) {
-}
-
void LocationsBuilderX86_64::HandleCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 7ebce58f6c..d7ce7c649f 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -350,8 +350,6 @@ class CodeGeneratorX86_64 : public CodeGenerator {
return GetLabelOf(block)->Position();
}
- Location GetStackLocation(HLoadLocal* load) const OVERRIDE;
-
void SetupBlockedRegisters() const OVERRIDE;
void DumpCoreRegister(std::ostream& stream, int reg) const OVERRIDE;
void DumpFloatingPointRegister(std::ostream& stream, int reg) const OVERRIDE;
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 6c551945e9..6412b24362 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -53,17 +53,17 @@ static inline int ARTRegCodeFromVIXL(int code) {
}
static inline vixl::Register XRegisterFrom(Location location) {
- DCHECK(location.IsRegister());
+ DCHECK(location.IsRegister()) << location;
return vixl::Register::XRegFromCode(VIXLRegCodeFromART(location.reg()));
}
static inline vixl::Register WRegisterFrom(Location location) {
- DCHECK(location.IsRegister());
+ DCHECK(location.IsRegister()) << location;
return vixl::Register::WRegFromCode(VIXLRegCodeFromART(location.reg()));
}
static inline vixl::Register RegisterFrom(Location location, Primitive::Type type) {
- DCHECK(type != Primitive::kPrimVoid && !Primitive::IsFloatingPointType(type));
+ DCHECK(type != Primitive::kPrimVoid && !Primitive::IsFloatingPointType(type)) << type;
return type == Primitive::kPrimLong ? XRegisterFrom(location) : WRegisterFrom(location);
}
@@ -77,17 +77,17 @@ static inline vixl::Register InputRegisterAt(HInstruction* instr, int input_inde
}
static inline vixl::FPRegister DRegisterFrom(Location location) {
- DCHECK(location.IsFpuRegister());
+ DCHECK(location.IsFpuRegister()) << location;
return vixl::FPRegister::DRegFromCode(location.reg());
}
static inline vixl::FPRegister SRegisterFrom(Location location) {
- DCHECK(location.IsFpuRegister());
+ DCHECK(location.IsFpuRegister()) << location;
return vixl::FPRegister::SRegFromCode(location.reg());
}
static inline vixl::FPRegister FPRegisterFrom(Location location, Primitive::Type type) {
- DCHECK(Primitive::IsFloatingPointType(type));
+ DCHECK(Primitive::IsFloatingPointType(type)) << type;
return type == Primitive::kPrimDouble ? DRegisterFrom(location) : SRegisterFrom(location);
}
@@ -124,7 +124,7 @@ static inline int64_t Int64ConstantFrom(Location location) {
} else if (instr->IsNullConstant()) {
return 0;
} else {
- DCHECK(instr->IsLongConstant());
+ DCHECK(instr->IsLongConstant()) << instr->DebugName();
return instr->AsLongConstant()->GetValue();
}
}
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 1e54a0adfa..d1a2a2649a 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -111,22 +111,21 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingNegation) {
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 2: IntConstant [5]\n"
- " 10: SuspendCheck\n"
- " 11: Goto 1\n"
+ " 2: IntConstant [3]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 5: Neg(2) [8]\n"
- " 8: Return(5)\n"
+ " 3: Neg(2) [4]\n"
+ " 4: Return(3)\n"
"BasicBlock 2, pred: 1\n"
- " 9: Exit\n";
+ " 5: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 2: IntConstant [5]\n", " 2: IntConstant\n" },
- { " 10: SuspendCheck\n", " 10: SuspendCheck\n"
- " 12: IntConstant [8]\n" },
- { " 5: Neg(2) [8]\n", removed },
- { " 8: Return(5)\n", " 8: Return(12)\n" }
+ { " 2: IntConstant [3]\n", " 2: IntConstant\n"
+ " 6: IntConstant [4]\n" },
+ { " 3: Neg(2) [4]\n", removed },
+ { " 4: Return(3)\n", " 4: Return(6)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -173,22 +172,21 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) {
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 4: LongConstant [7]\n"
- " 12: SuspendCheck\n"
- " 13: Goto 1\n"
+ " 2: LongConstant [3]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 7: Neg(4) [10]\n"
- " 10: Return(7)\n"
+ " 3: Neg(2) [4]\n"
+ " 4: Return(3)\n"
"BasicBlock 2, pred: 1\n"
- " 11: Exit\n";
+ " 5: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 4: LongConstant [7]\n", " 4: LongConstant\n" },
- { " 12: SuspendCheck\n", " 12: SuspendCheck\n"
- " 14: LongConstant [10]\n" },
- { " 7: Neg(4) [10]\n", removed },
- { " 10: Return(7)\n", " 10: Return(14)\n" }
+ { " 2: LongConstant [3]\n", " 2: LongConstant\n"
+ " 6: LongConstant [4]\n" },
+ { " 3: Neg(2) [4]\n", removed },
+ { " 4: Return(3)\n", " 4: Return(6)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -201,7 +199,7 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) {
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 4: LongConstant\n", removed },
+ { " 2: LongConstant\n", removed },
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -232,25 +230,24 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) {
Instruction::RETURN | 2 << 8);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n"
- " 5: IntConstant [9]\n"
- " 14: SuspendCheck\n"
- " 15: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 9: Add(3, 5) [12]\n"
- " 12: Return(9)\n"
- "BasicBlock 2, pred: 1\n"
- " 13: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: IntConstant [4]\n"
+ " 3: IntConstant [4]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Add(2, 3) [5]\n"
+ " 5: Return(4)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 6: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
- { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
- { " 14: SuspendCheck\n", " 14: SuspendCheck\n"
- " 16: IntConstant [12]\n" },
- { " 9: Add(3, 5) [12]\n", removed },
- { " 12: Return(9)\n", " 12: Return(16)\n" }
+ { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
+ { " 3: IntConstant [4]\n", " 3: IntConstant\n"
+ " 7: IntConstant [5]\n" },
+ { " 4: Add(2, 3) [5]\n", removed },
+ { " 5: Return(4)\n", " 5: Return(7)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -263,8 +260,8 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) {
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 3: IntConstant\n", removed },
- { " 5: IntConstant\n", removed }
+ { " 2: IntConstant\n", removed },
+ { " 3: IntConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -302,35 +299,34 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) {
Instruction::RETURN | 2 << 8);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n"
- " 5: IntConstant [9]\n"
- " 11: IntConstant [17]\n"
- " 13: IntConstant [17]\n"
- " 26: SuspendCheck\n"
- " 27: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 9: Add(3, 5) [21]\n"
- " 17: Add(11, 13) [21]\n"
- " 21: Add(9, 17) [24]\n"
- " 24: Return(21)\n"
- "BasicBlock 2, pred: 1\n"
- " 25: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: IntConstant [4]\n"
+ " 3: IntConstant [4]\n"
+ " 5: IntConstant [7]\n"
+ " 6: IntConstant [7]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Add(2, 3) [8]\n"
+ " 7: Add(5, 6) [8]\n"
+ " 8: Add(4, 7) [9]\n"
+ " 9: Return(8)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 10: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
- { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
- { " 11: IntConstant [17]\n", " 11: IntConstant\n" },
- { " 13: IntConstant [17]\n", " 13: IntConstant\n" },
- { " 26: SuspendCheck\n", " 26: SuspendCheck\n"
- " 28: IntConstant\n"
- " 29: IntConstant\n"
- " 30: IntConstant [24]\n" },
- { " 9: Add(3, 5) [21]\n", removed },
- { " 17: Add(11, 13) [21]\n", removed },
- { " 21: Add(9, 17) [24]\n", removed },
- { " 24: Return(21)\n", " 24: Return(30)\n" }
+ { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
+ { " 3: IntConstant [4]\n", " 3: IntConstant\n" },
+ { " 5: IntConstant [7]\n", " 5: IntConstant\n" },
+ { " 6: IntConstant [7]\n", " 6: IntConstant\n"
+ " 11: IntConstant\n"
+ " 12: IntConstant\n"
+ " 13: IntConstant [9]\n" },
+ { " 4: Add(2, 3) [8]\n", removed },
+ { " 7: Add(5, 6) [8]\n", removed },
+ { " 8: Add(4, 7) [9]\n", removed },
+ { " 9: Return(8)\n", " 9: Return(13)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -349,12 +345,12 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) {
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
+ { " 2: IntConstant\n", removed },
{ " 3: IntConstant\n", removed },
{ " 5: IntConstant\n", removed },
+ { " 6: IntConstant\n", removed },
{ " 11: IntConstant\n", removed },
- { " 13: IntConstant\n", removed },
- { " 28: IntConstant\n", removed },
- { " 29: IntConstant\n", removed }
+ { " 12: IntConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -384,25 +380,24 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) {
Instruction::RETURN | 2 << 8);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n"
- " 5: IntConstant [9]\n"
- " 14: SuspendCheck\n"
- " 15: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 9: Sub(3, 5) [12]\n"
- " 12: Return(9)\n"
- "BasicBlock 2, pred: 1\n"
- " 13: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: IntConstant [4]\n"
+ " 3: IntConstant [4]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Sub(2, 3) [5]\n"
+ " 5: Return(4)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 6: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
- { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
- { " 14: SuspendCheck\n", " 14: SuspendCheck\n"
- " 16: IntConstant [12]\n" },
- { " 9: Sub(3, 5) [12]\n", removed },
- { " 12: Return(9)\n", " 12: Return(16)\n" }
+ { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
+ { " 3: IntConstant [4]\n", " 3: IntConstant\n"
+ " 7: IntConstant [5]\n" },
+ { " 4: Sub(2, 3) [5]\n", removed },
+ { " 5: Return(4)\n", " 5: Return(7)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -415,8 +410,8 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) {
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 3: IntConstant\n", removed },
- { " 5: IntConstant\n", removed }
+ { " 2: IntConstant\n", removed },
+ { " 3: IntConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -448,25 +443,24 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) {
Instruction::RETURN_WIDE | 4 << 8);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 6: LongConstant [12]\n"
- " 8: LongConstant [12]\n"
- " 17: SuspendCheck\n"
- " 18: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 12: Add(6, 8) [15]\n"
- " 15: Return(12)\n"
- "BasicBlock 2, pred: 1\n"
- " 16: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: LongConstant [4]\n"
+ " 3: LongConstant [4]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Add(2, 3) [5]\n"
+ " 5: Return(4)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 6: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 6: LongConstant [12]\n", " 6: LongConstant\n" },
- { " 8: LongConstant [12]\n", " 8: LongConstant\n" },
- { " 17: SuspendCheck\n", " 17: SuspendCheck\n"
- " 19: LongConstant [15]\n" },
- { " 12: Add(6, 8) [15]\n", removed },
- { " 15: Return(12)\n", " 15: Return(19)\n" }
+ { " 2: LongConstant [4]\n", " 2: LongConstant\n" },
+ { " 3: LongConstant [4]\n", " 3: LongConstant\n"
+ " 7: LongConstant [5]\n" },
+ { " 4: Add(2, 3) [5]\n", removed },
+ { " 5: Return(4)\n", " 5: Return(7)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -479,8 +473,8 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) {
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 6: LongConstant\n", removed },
- { " 8: LongConstant\n", removed }
+ { " 2: LongConstant\n", removed },
+ { " 3: LongConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -513,25 +507,24 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) {
Instruction::RETURN_WIDE | 4 << 8);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 6: LongConstant [12]\n"
- " 8: LongConstant [12]\n"
- " 17: SuspendCheck\n"
- " 18: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 12: Sub(6, 8) [15]\n"
- " 15: Return(12)\n"
- "BasicBlock 2, pred: 1\n"
- " 16: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: LongConstant [4]\n"
+ " 3: LongConstant [4]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 4: Sub(2, 3) [5]\n"
+ " 5: Return(4)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 6: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 6: LongConstant [12]\n", " 6: LongConstant\n" },
- { " 8: LongConstant [12]\n", " 8: LongConstant\n" },
- { " 17: SuspendCheck\n", " 17: SuspendCheck\n"
- " 19: LongConstant [15]\n" },
- { " 12: Sub(6, 8) [15]\n", removed },
- { " 15: Return(12)\n", " 15: Return(19)\n" }
+ { " 2: LongConstant [4]\n", " 2: LongConstant\n" },
+ { " 3: LongConstant [4]\n", " 3: LongConstant\n"
+ " 7: LongConstant [5]\n" },
+ { " 4: Sub(2, 3) [5]\n", removed },
+ { " 5: Return(4)\n", " 5: Return(7)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -544,8 +537,8 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) {
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 6: LongConstant\n", removed },
- { " 8: LongConstant\n", removed }
+ { " 2: LongConstant\n", removed },
+ { " 3: LongConstant\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
@@ -593,46 +586,45 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) {
Instruction::RETURN | 2 << 8);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n" // v0 <- 1
- " 5: IntConstant [9]\n" // v1 <- 2
- " 13: IntConstant [14]\n" // const 5
- " 18: IntConstant [19]\n" // const 4
- " 23: IntConstant [24]\n" // const 8
- " 29: SuspendCheck\n"
- " 30: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 3\n"
- " 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 1 + 2 = 3
- " 11: Goto 3\n" // goto L2
- "BasicBlock 2, pred: 3, succ: 4\n" // L1:
- " 14: Add(19, 13) [24]\n" // v1 <- v0 + 3 = 7 + 5 = 12
- " 16: Goto 4\n" // goto L3
- "BasicBlock 3, pred: 1, succ: 2\n" // L2:
- " 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 3 + 4 = 7
- " 21: Goto 2\n" // goto L1
- "BasicBlock 4, pred: 2, succ: 5\n" // L3:
- " 24: Add(14, 23) [27]\n" // v2 <- v1 + 4 = 12 + 8 = 20
- " 27: Return(24)\n" // return v2
- "BasicBlock 5, pred: 4\n"
- " 28: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: IntConstant [4]\n" // v0 <- 1
+ " 3: IntConstant [4]\n" // v1 <- 2
+ " 6: IntConstant [7]\n" // const 5
+ " 9: IntConstant [10]\n" // const 4
+ " 12: IntConstant [13]\n" // const 8
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
+ " 4: Add(2, 3) [7]\n" // v2 <- v0 + v1 = 1 + 2 = 3
+ " 5: Goto 3\n" // goto L2
+ "BasicBlock 2, pred: 3, succ: 4\n" // L1:
+ " 10: Add(7, 9) [13]\n" // v1 <- v0 + 3 = 7 + 5 = 12
+ " 11: Goto 4\n" // goto L3
+ "BasicBlock 3, pred: 1, succ: 2\n" // L2:
+ " 7: Add(4, 6) [10]\n" // v0 <- v2 + 2 = 3 + 4 = 7
+ " 8: Goto 2\n" // goto L1
+ "BasicBlock 4, pred: 2, succ: 5\n" // L3:
+ " 13: Add(10, 12) [14]\n" // v2 <- v1 + 4 = 12 + 8 = 20
+ " 14: Return(13)\n" // return v2
+ "BasicBlock 5, pred: 4\n"
+ " 15: Exit\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [9]\n", " 3: IntConstant\n" },
- { " 5: IntConstant [9]\n", " 5: IntConstant\n" },
- { " 13: IntConstant [14]\n", " 13: IntConstant\n" },
- { " 18: IntConstant [19]\n", " 18: IntConstant\n" },
- { " 23: IntConstant [24]\n", " 23: IntConstant\n" },
- { " 29: SuspendCheck\n", " 29: SuspendCheck\n"
- " 31: IntConstant\n"
- " 32: IntConstant\n"
- " 33: IntConstant\n"
- " 34: IntConstant [27]\n" },
- { " 9: Add(3, 5) [19]\n", removed },
- { " 14: Add(19, 13) [24]\n", removed },
- { " 19: Add(9, 18) [14]\n", removed },
- { " 24: Add(14, 23) [27]\n", removed },
- { " 27: Return(24)\n", " 27: Return(34)\n"}
+ { " 2: IntConstant [4]\n", " 2: IntConstant\n" },
+ { " 3: IntConstant [4]\n", " 3: IntConstant\n" },
+ { " 6: IntConstant [7]\n", " 6: IntConstant\n" },
+ { " 9: IntConstant [10]\n", " 9: IntConstant\n" },
+ { " 12: IntConstant [13]\n", " 12: IntConstant\n"
+ " 16: IntConstant\n"
+ " 17: IntConstant\n"
+ " 18: IntConstant\n"
+ " 19: IntConstant [14]\n" },
+ { " 4: Add(2, 3) [7]\n", removed },
+ { " 10: Add(7, 9) [13]\n", removed },
+ { " 7: Add(4, 6) [10]\n", removed },
+ { " 13: Add(10, 12) [14]\n", removed },
+ { " 14: Return(13)\n", " 14: Return(19)\n"}
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -654,14 +646,14 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) {
// Expected difference after dead code elimination.
std::string expected_after_dce =
- "BasicBlock 0, succ: 1\n"
- " 29: SuspendCheck\n"
- " 34: IntConstant [27]\n"
- " 30: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 5\n"
- " 27: Return(34)\n"
- "BasicBlock 5, pred: 1\n"
- " 28: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 19: IntConstant [14]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5\n"
+ " 14: Return(19)\n"
+ "BasicBlock 5, pred: 1\n"
+ " 15: Exit\n";
TestCode(data,
expected_before,
@@ -693,31 +685,31 @@ TEST_F(ConstantFoldingTest, ConstantCondition) {
Instruction::RETURN_VOID);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [15, 22, 8]\n"
- " 5: IntConstant [22, 8]\n"
- " 19: SuspendCheck\n"
- " 20: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 5, 2\n"
- " 8: GreaterThanOrEqual(3, 5) [9]\n"
- " 9: If(8)\n"
- "BasicBlock 2, pred: 1, succ: 3\n"
- " 12: Goto 3\n"
- "BasicBlock 3, pred: 5, 2, succ: 4\n"
- " 22: Phi(5, 3) [15]\n"
- " 15: Add(22, 3)\n"
- " 17: ReturnVoid\n"
- "BasicBlock 4, pred: 3\n"
- " 18: Exit\n"
- "BasicBlock 5, pred: 1, succ: 3\n"
- " 21: Goto 3\n";
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9, 8, 5]\n"
+ " 4: IntConstant [8, 5]\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5, 2\n"
+ " 5: GreaterThanOrEqual(3, 4) [6]\n"
+ " 6: If(5)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 7: Goto 3\n"
+ "BasicBlock 3, pred: 5, 2, succ: 4\n"
+ " 8: Phi(4, 3) [9]\n"
+ " 9: Add(8, 3)\n"
+ " 10: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 11: Exit\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 0: Goto 3\n";
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [9, 15, 22]\n" },
- { " 5: IntConstant [22, 8]\n", " 5: IntConstant [22]\n" },
- { " 8: GreaterThanOrEqual(3, 5) [9]\n", removed },
- { " 9: If(8)\n", " 9: If(3)\n" }
+ { " 3: IntConstant [9, 8, 5]\n", " 3: IntConstant [6, 9, 8]\n" },
+ { " 4: IntConstant [8, 5]\n", " 4: IntConstant [8]\n" },
+ { " 5: GreaterThanOrEqual(3, 4) [6]\n", removed },
+ { " 6: If(5)\n", " 6: If(3)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
@@ -730,13 +722,13 @@ TEST_F(ConstantFoldingTest, ConstantCondition) {
// Expected graph after dead code elimination.
std::string expected_after_dce =
- "BasicBlock 0, succ: 1\n"
- " 19: SuspendCheck\n"
- " 20: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 4\n"
- " 17: ReturnVoid\n"
- "BasicBlock 4, pred: 1\n"
- " 18: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 4\n"
+ " 10: ReturnVoid\n"
+ "BasicBlock 4, pred: 1\n"
+ " 11: Exit\n";
TestCode(data,
expected_before,
@@ -766,7 +758,10 @@ TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) {
HInstruction* parameter = new (&allocator_) HParameterValue(
graph_->GetDexFile(), 0, 0, Primitive::kPrimInt, true);
entry_block->AddInstruction(parameter);
+ entry_block->AddInstruction(new (&allocator_) HGoto());
+
HInstruction* zero = graph_->GetIntConstant(0);
+
HInstruction* last;
block->AddInstruction(last = new (&allocator_) HAbove(zero, parameter));
block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
@@ -784,70 +779,70 @@ TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) {
block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
block->AddInstruction(last = new (&allocator_) HBelowOrEqual(parameter, zero));
block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0));
-
- entry_block->AddInstruction(new (&allocator_) HGoto());
block->AddInstruction(new (&allocator_) HReturn(zero));
+
exit_block->AddInstruction(new (&allocator_) HExit());
graph_->BuildDominatorTree();
const std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 0: ParameterValue [17, 17, 16, 15, 15, 14, 13, 13, 12, 11, 11, 10, 9, 9, "
- "8, 7, 7, 6, 5, 5, 4, 3, 3, 2]\n"
- " 1: IntConstant [19, 16, 14, 12, 10, 8, 6, 4, 2]\n"
- " 18: Goto 1\n"
+ " 0: ParameterValue [18, 18, 17, 16, 16, 15, 14, 14, 13, 12, 12, 11, 10, 10, 9, "
+ "8, 8, 7, 6, 6, 5, 4, 4, 3]\n"
+ " 2: IntConstant [19, 17, 15, 13, 11, 9, 7, 5, 3]\n"
+ " 1: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 2: Above(1, 0) [3]\n"
- " 3: Select(0, 0, 2)\n"
- " 4: Above(0, 1) [5]\n"
- " 5: Select(0, 0, 4)\n"
- " 6: AboveOrEqual(1, 0) [7]\n"
- " 7: Select(0, 0, 6)\n"
- " 8: AboveOrEqual(0, 1) [9]\n"
- " 9: Select(0, 0, 8)\n"
- " 10: Below(1, 0) [11]\n"
- " 11: Select(0, 0, 10)\n"
- " 12: Below(0, 1) [13]\n"
- " 13: Select(0, 0, 12)\n"
- " 14: BelowOrEqual(1, 0) [15]\n"
- " 15: Select(0, 0, 14)\n"
- " 16: BelowOrEqual(0, 1) [17]\n"
- " 17: Select(0, 0, 16)\n"
- " 19: Return(1)\n"
+ " 3: Above(2, 0) [4]\n"
+ " 4: Select(0, 0, 3)\n"
+ " 5: Above(0, 2) [6]\n"
+ " 6: Select(0, 0, 5)\n"
+ " 7: AboveOrEqual(2, 0) [8]\n"
+ " 8: Select(0, 0, 7)\n"
+ " 9: AboveOrEqual(0, 2) [10]\n"
+ " 10: Select(0, 0, 9)\n"
+ " 11: Below(2, 0) [12]\n"
+ " 12: Select(0, 0, 11)\n"
+ " 13: Below(0, 2) [14]\n"
+ " 14: Select(0, 0, 13)\n"
+ " 15: BelowOrEqual(2, 0) [16]\n"
+ " 16: Select(0, 0, 15)\n"
+ " 17: BelowOrEqual(0, 2) [18]\n"
+ " 18: Select(0, 0, 17)\n"
+ " 19: Return(2)\n"
"BasicBlock 2, pred: 1\n"
" 20: Exit\n";
const std::string expected_after_cf =
"BasicBlock 0, succ: 1\n"
- " 0: ParameterValue [17, 17, 16, 15, 15, 13, 13, 11, 11, 10, 9, 9, 7, 7, 6, 5, 5, 4, 3, 3]\n"
- " 1: IntConstant [13, 3, 19, 16, 10, 6, 4]\n"
- " 21: IntConstant [15, 9]\n"
- " 18: Goto 1\n"
+ " 0: ParameterValue [18, 18, 17, 16, 16, 14, 14, 12, 12, 11, 10, 10, "
+ "8, 8, 7, 6, 6, 5, 4, 4]\n"
+ " 2: IntConstant [14, 4, 19, 17, 11, 7, 5]\n"
+ " 21: IntConstant [16, 10]\n"
+ " 1: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 3: Select(0, 0, 1)\n"
- " 4: Above(0, 1) [5]\n"
- " 5: Select(0, 0, 4)\n"
- " 6: AboveOrEqual(1, 0) [7]\n"
- " 7: Select(0, 0, 6)\n"
- " 9: Select(0, 0, 21)\n"
- " 10: Below(1, 0) [11]\n"
- " 11: Select(0, 0, 10)\n"
- " 13: Select(0, 0, 1)\n"
- " 15: Select(0, 0, 21)\n"
- " 16: BelowOrEqual(0, 1) [17]\n"
- " 17: Select(0, 0, 16)\n"
- " 19: Return(1)\n"
+ " 4: Select(0, 0, 2)\n"
+ " 5: Above(0, 2) [6]\n"
+ " 6: Select(0, 0, 5)\n"
+ " 7: AboveOrEqual(2, 0) [8]\n"
+ " 8: Select(0, 0, 7)\n"
+ " 10: Select(0, 0, 21)\n"
+ " 11: Below(2, 0) [12]\n"
+ " 12: Select(0, 0, 11)\n"
+ " 14: Select(0, 0, 2)\n"
+ " 16: Select(0, 0, 21)\n"
+ " 17: BelowOrEqual(0, 2) [18]\n"
+ " 18: Select(0, 0, 17)\n"
+ " 19: Return(2)\n"
"BasicBlock 2, pred: 1\n"
" 20: Exit\n";
const std::string expected_after_dce =
"BasicBlock 0, succ: 1\n"
" 0: ParameterValue\n"
- " 1: IntConstant [19]\n"
- " 18: Goto 1\n"
+ " 2: IntConstant [19]\n"
+ " 1: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 19: Return(1)\n"
+ " 19: Return(2)\n"
"BasicBlock 2, pred: 1\n"
" 20: Exit\n";
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 83e724ba29..fe52aacef7 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -78,30 +78,30 @@ TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) {
Instruction::RETURN_VOID);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [15, 22, 8]\n"
- " 5: IntConstant [22, 8]\n"
- " 19: SuspendCheck\n"
- " 20: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 5, 2\n"
- " 8: GreaterThanOrEqual(3, 5) [9]\n"
- " 9: If(8)\n"
- "BasicBlock 2, pred: 1, succ: 3\n"
- " 12: Goto 3\n"
- "BasicBlock 3, pred: 5, 2, succ: 4\n"
- " 22: Phi(5, 3) [15]\n"
- " 15: Add(22, 3)\n"
- " 17: ReturnVoid\n"
- "BasicBlock 4, pred: 3\n"
- " 18: Exit\n"
- "BasicBlock 5, pred: 1, succ: 3\n"
- " 21: Goto 3\n";
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [9, 8, 5]\n"
+ " 4: IntConstant [8, 5]\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5, 2\n"
+ " 5: GreaterThanOrEqual(3, 4) [6]\n"
+ " 6: If(5)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 7: Goto 3\n"
+ "BasicBlock 3, pred: 5, 2, succ: 4\n"
+ " 8: Phi(4, 3) [9]\n"
+ " 9: Add(8, 3)\n"
+ " 10: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 11: Exit\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 0: Goto 3\n";
// Expected difference after dead code elimination.
diff_t expected_diff = {
- { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [22, 8]\n" },
- { " 22: Phi(5, 3) [15]\n", " 22: Phi(5, 3)\n" },
- { " 15: Add(22, 3)\n", removed }
+ { " 3: IntConstant [9, 8, 5]\n", " 3: IntConstant [8, 5]\n" },
+ { " 8: Phi(4, 3) [9]\n", " 8: Phi(4, 3)\n" },
+ { " 9: Add(8, 3)\n", removed }
};
std::string expected_after = Patch(expected_before, expected_diff);
@@ -144,37 +144,37 @@ TEST_F(DeadCodeEliminationTest, AdditionsAndInconditionalJumps) {
Instruction::RETURN_VOID);
std::string expected_before =
- "BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n"
- " 5: IntConstant [9]\n"
- " 13: IntConstant [14]\n"
- " 18: IntConstant [19]\n"
- " 23: IntConstant [24]\n"
- " 28: SuspendCheck\n"
- " 29: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 3\n"
- " 9: Add(3, 5) [19]\n"
- " 11: Goto 3\n"
- "BasicBlock 2, pred: 3, succ: 4\n"
- " 14: Add(19, 13) [24]\n"
- " 16: Goto 4\n"
- "BasicBlock 3, pred: 1, succ: 2\n"
- " 19: Add(9, 18) [14]\n"
- " 21: Goto 2\n"
- "BasicBlock 4, pred: 2, succ: 5\n"
- " 24: Add(14, 23)\n"
- " 26: ReturnVoid\n"
- "BasicBlock 5, pred: 4\n"
- " 27: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: IntConstant [4]\n"
+ " 3: IntConstant [4]\n"
+ " 6: IntConstant [7]\n"
+ " 9: IntConstant [10]\n"
+ " 12: IntConstant [13]\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
+ " 4: Add(2, 3) [7]\n"
+ " 5: Goto 3\n"
+ "BasicBlock 2, pred: 3, succ: 4\n"
+ " 10: Add(7, 9) [13]\n"
+ " 11: Goto 4\n"
+ "BasicBlock 3, pred: 1, succ: 2\n"
+ " 7: Add(4, 6) [10]\n"
+ " 8: Goto 2\n"
+ "BasicBlock 4, pred: 2, succ: 5\n"
+ " 13: Add(10, 12)\n"
+ " 14: ReturnVoid\n"
+ "BasicBlock 5, pred: 4\n"
+ " 15: Exit\n";
std::string expected_after =
- "BasicBlock 0, succ: 1\n"
- " 28: SuspendCheck\n"
- " 29: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 5\n"
- " 26: ReturnVoid\n"
- "BasicBlock 5, pred: 1\n"
- " 27: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5\n"
+ " 14: ReturnVoid\n"
+ "BasicBlock 5, pred: 1\n"
+ " 15: Exit\n";
TestCode(data, expected_before, expected_after);
}
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index c790d013b5..9ea4b2dab4 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -27,6 +27,21 @@
namespace art {
+static bool IsAllowedToJumpToExitBlock(HInstruction* instruction) {
+ return instruction->IsThrow() || instruction->IsReturn() || instruction->IsReturnVoid();
+}
+
+static bool IsExitTryBoundaryIntoExitBlock(HBasicBlock* block) {
+ if (!block->IsSingleTryBoundary()) {
+ return false;
+ }
+
+ HTryBoundary* boundary = block->GetLastInstruction()->AsTryBoundary();
+ return block->GetPredecessors().size() == 1u &&
+ boundary->GetNormalFlowSuccessor()->IsExitBlock() &&
+ !boundary->IsEntry();
+}
+
void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
current_block_ = block;
@@ -85,28 +100,17 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
block->GetBlockId()));
}
- // Ensure that only Return(Void) and Throw jump to Exit. An exiting
- // TryBoundary may be between a Throw and the Exit if the Throw is in a try.
+ // Ensure that only Return(Void) and Throw jump to Exit. An exiting TryBoundary
+ // may be between the instructions if the Throw/Return(Void) is in a try block.
if (block->IsExitBlock()) {
for (HBasicBlock* predecessor : block->GetPredecessors()) {
- if (predecessor->IsSingleTryBoundary()
- && !predecessor->GetLastInstruction()->AsTryBoundary()->IsEntry()) {
- HBasicBlock* real_predecessor = predecessor->GetSinglePredecessor();
- HInstruction* last_instruction = real_predecessor->GetLastInstruction();
- if (!last_instruction->IsThrow()) {
- AddError(StringPrintf("Unexpected TryBoundary between %s:%d and Exit.",
- last_instruction->DebugName(),
- last_instruction->GetId()));
- }
- } else {
- HInstruction* last_instruction = predecessor->GetLastInstruction();
- if (!last_instruction->IsReturn()
- && !last_instruction->IsReturnVoid()
- && !last_instruction->IsThrow()) {
- AddError(StringPrintf("Unexpected instruction %s:%d jumps into the exit block.",
- last_instruction->DebugName(),
- last_instruction->GetId()));
- }
+ HInstruction* last_instruction = IsExitTryBoundaryIntoExitBlock(predecessor) ?
+ predecessor->GetSinglePredecessor()->GetLastInstruction() :
+ predecessor->GetLastInstruction();
+ if (!IsAllowedToJumpToExitBlock(last_instruction)) {
+ AddError(StringPrintf("Unexpected instruction %s:%d jumps into the exit block.",
+ last_instruction->DebugName(),
+ last_instruction->GetId()));
}
}
}
@@ -176,16 +180,15 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
// predecessors). Exceptional edges are synthesized and hence
// not accounted for.
if (block->GetSuccessors().size() > 1) {
- for (HBasicBlock* successor : block->GetNormalSuccessors()) {
- if (successor->IsExitBlock() &&
- block->IsSingleTryBoundary() &&
- block->GetPredecessors().size() == 1u &&
- block->GetSinglePredecessor()->GetLastInstruction()->IsThrow()) {
- // Allowed critical edge Throw->TryBoundary->Exit.
- } else if (successor->GetPredecessors().size() > 1) {
- AddError(StringPrintf("Critical edge between blocks %d and %d.",
- block->GetBlockId(),
- successor->GetBlockId()));
+ if (IsExitTryBoundaryIntoExitBlock(block)) {
+ // Allowed critical edge (Throw/Return/ReturnVoid)->TryBoundary->Exit.
+ } else {
+ for (HBasicBlock* successor : block->GetNormalSuccessors()) {
+ if (successor->GetPredecessors().size() > 1) {
+ AddError(StringPrintf("Critical edge between blocks %d and %d.",
+ block->GetBlockId(),
+ successor->GetBlockId()));
+ }
}
}
}
@@ -505,7 +508,8 @@ void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
void GraphChecker::VisitReturn(HReturn* ret) {
VisitInstruction(ret);
- if (!ret->GetBlock()->GetSingleSuccessor()->IsExitBlock()) {
+ HBasicBlock* successor = ret->GetBlock()->GetSingleSuccessor();
+ if (!successor->IsExitBlock() && !IsExitTryBoundaryIntoExitBlock(successor)) {
AddError(StringPrintf("%s:%d does not jump to the exit block.",
ret->DebugName(),
ret->GetId()));
@@ -514,7 +518,8 @@ void GraphChecker::VisitReturn(HReturn* ret) {
void GraphChecker::VisitReturnVoid(HReturnVoid* ret) {
VisitInstruction(ret);
- if (!ret->GetBlock()->GetSingleSuccessor()->IsExitBlock()) {
+ HBasicBlock* successor = ret->GetBlock()->GetSingleSuccessor();
+ if (!successor->IsExitBlock() && !IsExitTryBoundaryIntoExitBlock(successor)) {
AddError(StringPrintf("%s:%d does not jump to the exit block.",
ret->DebugName(),
ret->GetId()));
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 56dc08826b..6abf00e21a 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -357,8 +357,10 @@ TEST_F(GVNTest, LoopSideEffects) {
Primitive::kPrimBoolean);
entry->AddInstruction(parameter);
entry->AddInstruction(new (&allocator) HGoto());
+ outer_loop_header->AddInstruction(new (&allocator) HSuspendCheck());
outer_loop_header->AddInstruction(new (&allocator) HIf(parameter));
outer_loop_body->AddInstruction(new (&allocator) HGoto());
+ inner_loop_header->AddInstruction(new (&allocator) HSuspendCheck());
inner_loop_header->AddInstruction(new (&allocator) HIf(parameter));
inner_loop_body->AddInstruction(new (&allocator) HGoto());
inner_loop_exit->AddInstruction(new (&allocator) HGoto());
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index fce9ab8b85..33803c1093 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1061,17 +1061,24 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
caller_instruction_counter);
callee_graph->SetArtMethod(resolved_method);
- OptimizingCompilerStats inline_stats;
+ // When they are needed, allocate `inline_stats` on the heap instead
+ // of on the stack, as Clang might produce a stack frame too large
+ // for this function, that would not fit the requirements of the
+ // `-Wframe-larger-than` option.
+ std::unique_ptr<OptimizingCompilerStats> inline_stats =
+ (stats_ == nullptr) ? nullptr : MakeUnique<OptimizingCompilerStats>();
HGraphBuilder builder(callee_graph,
&dex_compilation_unit,
&outer_compilation_unit_,
resolved_method->GetDexFile(),
+ *code_item,
compiler_driver_,
- &inline_stats,
+ inline_stats.get(),
resolved_method->GetQuickenedInfo(),
- dex_cache);
+ dex_cache,
+ handles_);
- if (builder.BuildGraph(*code_item, handles_) != kAnalysisSuccess) {
+ if (builder.BuildGraph() != kAnalysisSuccess) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be built, so cannot be inlined";
return false;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
new file mode 100644
index 0000000000..c5f2342027
--- /dev/null
+++ b/compiler/optimizing/instruction_builder.cc
@@ -0,0 +1,2681 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "instruction_builder.h"
+
+#include "bytecode_utils.h"
+#include "class_linker.h"
+#include "driver/compiler_options.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+
+void HInstructionBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
+ if (compilation_stats_ != nullptr) {
+ compilation_stats_->RecordStat(compilation_stat);
+ }
+}
+
+HBasicBlock* HInstructionBuilder::FindBlockStartingAt(uint32_t dex_pc) const {
+ return block_builder_->GetBlockAt(dex_pc);
+}
+
+ArenaVector<HInstruction*>* HInstructionBuilder::GetLocalsFor(HBasicBlock* block) {
+ ArenaVector<HInstruction*>* locals = &locals_for_[block->GetBlockId()];
+ const size_t vregs = graph_->GetNumberOfVRegs();
+ if (locals->size() != vregs) {
+ locals->resize(vregs, nullptr);
+
+ if (block->IsCatchBlock()) {
+ // We record incoming inputs of catch phis at throwing instructions and
+ // must therefore eagerly create the phis. Phis for undefined vregs will
+ // be deleted when the first throwing instruction with the vreg undefined
+ // is encountered. Unused phis will be removed by dead phi analysis.
+ for (size_t i = 0; i < vregs; ++i) {
+ // No point in creating the catch phi if it is already undefined at
+ // the first throwing instruction.
+ HInstruction* current_local_value = (*current_locals_)[i];
+ if (current_local_value != nullptr) {
+ HPhi* phi = new (arena_) HPhi(
+ arena_,
+ i,
+ 0,
+ current_local_value->GetType());
+ block->AddPhi(phi);
+ (*locals)[i] = phi;
+ }
+ }
+ }
+ }
+ return locals;
+}
+
+HInstruction* HInstructionBuilder::ValueOfLocalAt(HBasicBlock* block, size_t local) {
+ ArenaVector<HInstruction*>* locals = GetLocalsFor(block);
+ return (*locals)[local];
+}
+
+void HInstructionBuilder::InitializeBlockLocals() {
+ current_locals_ = GetLocalsFor(current_block_);
+
+ if (current_block_->IsCatchBlock()) {
+ // Catch phis were already created and inputs collected from throwing sites.
+ if (kIsDebugBuild) {
+ // Make sure there was at least one throwing instruction which initialized
+ // locals (guaranteed by HGraphBuilder) and that all try blocks have been
+ // visited already (from HTryBoundary scoping and reverse post order).
+ bool catch_block_visited = false;
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* current = it.Current();
+ if (current == current_block_) {
+ catch_block_visited = true;
+ } else if (current->IsTryBlock()) {
+ const HTryBoundary& try_entry = current->GetTryCatchInformation()->GetTryEntry();
+ if (try_entry.HasExceptionHandler(*current_block_)) {
+ DCHECK(!catch_block_visited) << "Catch block visited before its try block.";
+ }
+ }
+ }
+ DCHECK_EQ(current_locals_->size(), graph_->GetNumberOfVRegs())
+ << "No instructions throwing into a live catch block.";
+ }
+ } else if (current_block_->IsLoopHeader()) {
+ // If the block is a loop header, we know we only have visited the pre header
+ // because we are visiting in reverse post order. We create phis for all initialized
+ // locals from the pre header. Their inputs will be populated at the end of
+ // the analysis.
+ for (size_t local = 0; local < current_locals_->size(); ++local) {
+ HInstruction* incoming =
+ ValueOfLocalAt(current_block_->GetLoopInformation()->GetPreHeader(), local);
+ if (incoming != nullptr) {
+ HPhi* phi = new (arena_) HPhi(
+ arena_,
+ local,
+ 0,
+ incoming->GetType());
+ current_block_->AddPhi(phi);
+ (*current_locals_)[local] = phi;
+ }
+ }
+
+ // Save the loop header so that the last phase of the analysis knows which
+ // blocks need to be updated.
+ loop_headers_.push_back(current_block_);
+ } else if (current_block_->GetPredecessors().size() > 0) {
+ // All predecessors have already been visited because we are visiting in reverse post order.
+ // We merge the values of all locals, creating phis if those values differ.
+ for (size_t local = 0; local < current_locals_->size(); ++local) {
+ bool one_predecessor_has_no_value = false;
+ bool is_different = false;
+ HInstruction* value = ValueOfLocalAt(current_block_->GetPredecessors()[0], local);
+
+ for (HBasicBlock* predecessor : current_block_->GetPredecessors()) {
+ HInstruction* current = ValueOfLocalAt(predecessor, local);
+ if (current == nullptr) {
+ one_predecessor_has_no_value = true;
+ break;
+ } else if (current != value) {
+ is_different = true;
+ }
+ }
+
+ if (one_predecessor_has_no_value) {
+ // If one predecessor has no value for this local, we trust the verifier has
+ // successfully checked that there is a store dominating any read after this block.
+ continue;
+ }
+
+ if (is_different) {
+ HInstruction* first_input = ValueOfLocalAt(current_block_->GetPredecessors()[0], local);
+ HPhi* phi = new (arena_) HPhi(
+ arena_,
+ local,
+ current_block_->GetPredecessors().size(),
+ first_input->GetType());
+ for (size_t i = 0; i < current_block_->GetPredecessors().size(); i++) {
+ HInstruction* pred_value = ValueOfLocalAt(current_block_->GetPredecessors()[i], local);
+ phi->SetRawInputAt(i, pred_value);
+ }
+ current_block_->AddPhi(phi);
+ value = phi;
+ }
+ (*current_locals_)[local] = value;
+ }
+ }
+}
+
+void HInstructionBuilder::PropagateLocalsToCatchBlocks() {
+ const HTryBoundary& try_entry = current_block_->GetTryCatchInformation()->GetTryEntry();
+ for (HBasicBlock* catch_block : try_entry.GetExceptionHandlers()) {
+ ArenaVector<HInstruction*>* handler_locals = GetLocalsFor(catch_block);
+ DCHECK_EQ(handler_locals->size(), current_locals_->size());
+ for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
+ HInstruction* handler_value = (*handler_locals)[vreg];
+ if (handler_value == nullptr) {
+ // Vreg was undefined at a previously encountered throwing instruction
+ // and the catch phi was deleted. Do not record the local value.
+ continue;
+ }
+ DCHECK(handler_value->IsPhi());
+
+ HInstruction* local_value = (*current_locals_)[vreg];
+ if (local_value == nullptr) {
+ // This is the first instruction throwing into `catch_block` where
+ // `vreg` is undefined. Delete the catch phi.
+ catch_block->RemovePhi(handler_value->AsPhi());
+ (*handler_locals)[vreg] = nullptr;
+ } else {
+ // Vreg has been defined at all instructions throwing into `catch_block`
+ // encountered so far. Record the local value in the catch phi.
+ handler_value->AsPhi()->AddInput(local_value);
+ }
+ }
+ }
+}
+
+void HInstructionBuilder::AppendInstruction(HInstruction* instruction) {
+ current_block_->AddInstruction(instruction);
+ InitializeInstruction(instruction);
+}
+
+void HInstructionBuilder::InsertInstructionAtTop(HInstruction* instruction) {
+ if (current_block_->GetInstructions().IsEmpty()) {
+ current_block_->AddInstruction(instruction);
+ } else {
+ current_block_->InsertInstructionBefore(instruction, current_block_->GetFirstInstruction());
+ }
+ InitializeInstruction(instruction);
+}
+
+void HInstructionBuilder::InitializeInstruction(HInstruction* instruction) {
+ if (instruction->NeedsEnvironment()) {
+ HEnvironment* environment = new (arena_) HEnvironment(
+ arena_,
+ current_locals_->size(),
+ graph_->GetDexFile(),
+ graph_->GetMethodIdx(),
+ instruction->GetDexPc(),
+ graph_->GetInvokeType(),
+ instruction);
+ environment->CopyFrom(*current_locals_);
+ instruction->SetRawEnvironment(environment);
+ }
+}
+
+void HInstructionBuilder::SetLoopHeaderPhiInputs() {
+ for (size_t i = loop_headers_.size(); i > 0; --i) {
+ HBasicBlock* block = loop_headers_[i - 1];
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ size_t vreg = phi->GetRegNumber();
+ for (HBasicBlock* predecessor : block->GetPredecessors()) {
+ HInstruction* value = ValueOfLocalAt(predecessor, vreg);
+ if (value == nullptr) {
+ // Vreg is undefined at this predecessor. Mark it dead and leave with
+ // fewer inputs than predecessors. SsaChecker will fail if not removed.
+ phi->SetDead();
+ break;
+ } else {
+ phi->AddInput(value);
+ }
+ }
+ }
+ }
+}
+
+static bool IsBlockPopulated(HBasicBlock* block) {
+ 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();
+ }
+}
+
+bool HInstructionBuilder::Build() {
+ locals_for_.resize(graph_->GetBlocks().size(),
+ ArenaVector<HInstruction*>(arena_->Adapter(kArenaAllocGraphBuilder)));
+
+ // 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.
+ const bool native_debuggable = compiler_driver_ != nullptr &&
+ compiler_driver_->GetCompilerOptions().GetNativeDebuggable();
+ ArenaBitVector* native_debug_info_locations = nullptr;
+ if (native_debuggable) {
+ const uint32_t num_instructions = code_item_.insns_size_in_code_units_;
+ native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false);
+ FindNativeDebugInfoLocations(native_debug_info_locations);
+ }
+
+ for (HReversePostOrderIterator block_it(*graph_); !block_it.Done(); block_it.Advance()) {
+ current_block_ = block_it.Current();
+ uint32_t block_dex_pc = current_block_->GetDexPc();
+
+ InitializeBlockLocals();
+
+ if (current_block_->IsEntryBlock()) {
+ InitializeParameters();
+ AppendInstruction(new (arena_) HSuspendCheck(0u));
+ AppendInstruction(new (arena_) HGoto(0u));
+ continue;
+ } else if (current_block_->IsExitBlock()) {
+ AppendInstruction(new (arena_) HExit());
+ continue;
+ } else if (current_block_->IsLoopHeader()) {
+ HSuspendCheck* suspend_check = new (arena_) HSuspendCheck(current_block_->GetDexPc());
+ current_block_->GetLoopInformation()->SetSuspendCheck(suspend_check);
+ // This is slightly odd because the loop header might not be empty (TryBoundary).
+ // But we're still creating the environment with locals from the top of the block.
+ InsertInstructionAtTop(suspend_check);
+ }
+
+ if (block_dex_pc == kNoDexPc || current_block_ != block_builder_->GetBlockAt(block_dex_pc)) {
+ // Synthetic block that does not need to be populated.
+ DCHECK(IsBlockPopulated(current_block_));
+ continue;
+ }
+
+ DCHECK(!IsBlockPopulated(current_block_));
+
+ for (CodeItemIterator it(code_item_, block_dex_pc); !it.Done(); it.Advance()) {
+ if (current_block_ == nullptr) {
+ // The previous instruction ended this block.
+ break;
+ }
+
+ uint32_t dex_pc = it.CurrentDexPc();
+ if (dex_pc != block_dex_pc && FindBlockStartingAt(dex_pc) != nullptr) {
+ // This dex_pc starts a new basic block.
+ break;
+ }
+
+ if (current_block_->IsTryBlock() && IsThrowingDexInstruction(it.CurrentInstruction())) {
+ PropagateLocalsToCatchBlocks();
+ }
+
+ if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
+ AppendInstruction(new (arena_) HNativeDebugInfo(dex_pc));
+ }
+
+ if (!ProcessDexInstruction(it.CurrentInstruction(), dex_pc)) {
+ return false;
+ }
+ }
+
+ 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 next block.
+ DCHECK_EQ(current_block_->GetSuccessors().size(), 1u);
+ AppendInstruction(new (arena_) HGoto());
+ }
+ }
+
+ SetLoopHeaderPhiInputs();
+
+ return true;
+}
+
+void HInstructionBuilder::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 {
+ static bool Position(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<ArenaBitVector*>(ctx)->SetBit(entry.address_);
+ return false;
+ }
+ };
+ 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_);
+ 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_));
+ const Instruction* next = inst->Next();
+ if (next < end) {
+ locations->SetBit(next->GetDexPc(code_item_.insns_));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+HInstruction* HInstructionBuilder::LoadLocal(uint32_t reg_number, Primitive::Type type) const {
+ HInstruction* value = (*current_locals_)[reg_number];
+ DCHECK(value != nullptr);
+
+ // If the operation requests a specific type, we make sure its input is of that type.
+ if (type != value->GetType()) {
+ if (Primitive::IsFloatingPointType(type)) {
+ return ssa_builder_->GetFloatOrDoubleEquivalent(value, type);
+ } else if (type == Primitive::kPrimNot) {
+ return ssa_builder_->GetReferenceTypeEquivalent(value);
+ }
+ }
+
+ return value;
+}
+
+void HInstructionBuilder::UpdateLocal(uint32_t reg_number, HInstruction* stored_value) {
+ Primitive::Type stored_type = stored_value->GetType();
+ DCHECK_NE(stored_type, Primitive::kPrimVoid);
+
+ // Storing into vreg `reg_number` may implicitly invalidate the surrounding
+ // registers. Consider the following cases:
+ // (1) Storing a wide value must overwrite previous values in both `reg_number`
+ // and `reg_number+1`. We store `nullptr` in `reg_number+1`.
+ // (2) If vreg `reg_number-1` holds a wide value, writing into `reg_number`
+ // must invalidate it. We store `nullptr` in `reg_number-1`.
+ // Consequently, storing a wide value into the high vreg of another wide value
+ // will invalidate both `reg_number-1` and `reg_number+1`.
+
+ if (reg_number != 0) {
+ HInstruction* local_low = (*current_locals_)[reg_number - 1];
+ if (local_low != nullptr && Primitive::Is64BitType(local_low->GetType())) {
+ // The vreg we are storing into was previously the high vreg of a pair.
+ // We need to invalidate its low vreg.
+ DCHECK((*current_locals_)[reg_number] == nullptr);
+ (*current_locals_)[reg_number - 1] = nullptr;
+ }
+ }
+
+ (*current_locals_)[reg_number] = stored_value;
+ if (Primitive::Is64BitType(stored_type)) {
+ // We are storing a pair. Invalidate the instruction in the high vreg.
+ (*current_locals_)[reg_number + 1] = nullptr;
+ }
+}
+
+void HInstructionBuilder::InitializeParameters() {
+ DCHECK(current_block_->IsEntryBlock());
+
+ // dex_compilation_unit_ is null only when unit testing.
+ if (dex_compilation_unit_ == nullptr) {
+ return;
+ }
+
+ const char* shorty = dex_compilation_unit_->GetShorty();
+ uint16_t number_of_parameters = graph_->GetNumberOfInVRegs();
+ uint16_t locals_index = graph_->GetNumberOfLocalVRegs();
+ uint16_t parameter_index = 0;
+
+ const DexFile::MethodId& referrer_method_id =
+ dex_file_->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
+ if (!dex_compilation_unit_->IsStatic()) {
+ // Add the implicit 'this' argument, not expressed in the signature.
+ HParameterValue* parameter = new (arena_) HParameterValue(*dex_file_,
+ referrer_method_id.class_idx_,
+ parameter_index++,
+ Primitive::kPrimNot,
+ true);
+ AppendInstruction(parameter);
+ UpdateLocal(locals_index++, parameter);
+ number_of_parameters--;
+ }
+
+ const DexFile::ProtoId& proto = dex_file_->GetMethodPrototype(referrer_method_id);
+ const DexFile::TypeList* arg_types = dex_file_->GetProtoParameters(proto);
+ for (int i = 0, shorty_pos = 1; i < number_of_parameters; i++) {
+ HParameterValue* parameter = new (arena_) HParameterValue(
+ *dex_file_,
+ arg_types->GetTypeItem(shorty_pos - 1).type_idx_,
+ parameter_index++,
+ Primitive::GetType(shorty[shorty_pos]),
+ false);
+ ++shorty_pos;
+ AppendInstruction(parameter);
+ // Store the parameter value in the local that the dex code will use
+ // to reference that parameter.
+ UpdateLocal(locals_index++, parameter);
+ if (Primitive::Is64BitType(parameter->GetType())) {
+ i++;
+ locals_index++;
+ parameter_index++;
+ }
+ }
+}
+
+template<typename T>
+void HInstructionBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+ HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
+ T* comparison = new (arena_) T(first, second, dex_pc);
+ AppendInstruction(comparison);
+ AppendInstruction(new (arena_) HIf(comparison, dex_pc));
+ current_block_ = nullptr;
+}
+
+template<typename T>
+void HInstructionBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+ T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
+ AppendInstruction(comparison);
+ AppendInstruction(new (arena_) HIf(comparison, dex_pc));
+ current_block_ = nullptr;
+}
+
+template<typename T>
+void HInstructionBuilder::Unop_12x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type);
+ AppendInstruction(new (arena_) T(type, first, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+void HInstructionBuilder::Conversion_12x(const Instruction& instruction,
+ Primitive::Type input_type,
+ Primitive::Type result_type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), input_type);
+ AppendInstruction(new (arena_) HTypeConversion(result_type, first, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HInstructionBuilder::Binop_23x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type);
+ HInstruction* second = LoadLocal(instruction.VRegC(), type);
+ AppendInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HInstructionBuilder::Binop_23x_shift(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type);
+ HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt);
+ AppendInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+void HInstructionBuilder::Binop_23x_cmp(const Instruction& instruction,
+ Primitive::Type type,
+ ComparisonBias bias,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), type);
+ HInstruction* second = LoadLocal(instruction.VRegC(), type);
+ AppendInstruction(new (arena_) HCompare(type, first, second, bias, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HInstructionBuilder::Binop_12x_shift(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), type);
+ HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
+ AppendInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HInstructionBuilder::Binop_12x(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegA(), type);
+ HInstruction* second = LoadLocal(instruction.VRegB(), type);
+ AppendInstruction(new (arena_) T(type, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HInstructionBuilder::Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s(), dex_pc);
+ if (reverse) {
+ std::swap(first, second);
+ }
+ AppendInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+template<typename T>
+void HInstructionBuilder::Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc) {
+ HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b(), dex_pc);
+ if (reverse) {
+ std::swap(first, second);
+ }
+ AppendInstruction(new (arena_) T(Primitive::kPrimInt, first, second, dex_pc));
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+}
+
+static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const CompilerDriver& driver) {
+ Thread* self = Thread::Current();
+ return cu->IsConstructor()
+ && 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 HInstructionBuilder::BuildSwitch(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
+ 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_));
+ AppendInstruction(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);
+ AppendInstruction(comparison);
+ AppendInstruction(new (arena_) HIf(comparison, dex_pc));
+
+ if (!it.IsLast()) {
+ current_block_ = FindBlockStartingAt(it.GetDexPcForCurrentIndex());
+ }
+ }
+ } else {
+ AppendInstruction(
+ new (arena_) HPackedSwitch(table.GetEntryAt(0), table.GetNumEntries(), value, dex_pc));
+ }
+
+ current_block_ = nullptr;
+}
+
+void HInstructionBuilder::BuildReturn(const Instruction& instruction,
+ Primitive::Type type,
+ uint32_t dex_pc) {
+ if (type == Primitive::kPrimVoid) {
+ if (graph_->ShouldGenerateConstructorBarrier()) {
+ // The compilation unit is null during testing.
+ if (dex_compilation_unit_ != nullptr) {
+ DCHECK(RequiresConstructorBarrier(dex_compilation_unit_, *compiler_driver_))
+ << "Inconsistent use of ShouldGenerateConstructorBarrier. Should not generate a barrier.";
+ }
+ AppendInstruction(new (arena_) HMemoryBarrier(kStoreStore, dex_pc));
+ }
+ AppendInstruction(new (arena_) HReturnVoid(dex_pc));
+ } else {
+ HInstruction* value = LoadLocal(instruction.VRegA(), type);
+ AppendInstruction(new (arena_) HReturn(value, dex_pc));
+ }
+ current_block_ = nullptr;
+}
+
+static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
+ switch (opcode) {
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ return kStatic;
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ return kDirect;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
+ return kVirtual;
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ return kInterface;
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_SUPER:
+ return kSuper;
+ default:
+ LOG(FATAL) << "Unexpected invoke opcode: " << opcode;
+ UNREACHABLE();
+ }
+}
+
+ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+
+ ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
+
+ ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
+ *dex_compilation_unit_->GetDexFile(),
+ method_idx,
+ dex_compilation_unit_->GetDexCache(),
+ class_loader,
+ /* referrer */ nullptr,
+ invoke_type);
+
+ if (UNLIKELY(resolved_method == nullptr)) {
+ // Clean up any exception left by type resolution.
+ soa.Self()->ClearException();
+ return nullptr;
+ }
+
+ // Check access. The class linker has a fast path for looking into the dex cache
+ // and does not check the access if it hits it.
+ if (compiling_class.Get() == nullptr) {
+ if (!resolved_method->IsPublic()) {
+ return nullptr;
+ }
+ } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
+ resolved_method,
+ dex_compilation_unit_->GetDexCache().Get(),
+ method_idx)) {
+ return nullptr;
+ }
+
+ // We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
+ // We need to look at the referrer's super class vtable. We need to do this to know if we need to
+ // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
+ // which require runtime handling.
+ if (invoke_type == kSuper) {
+ if (compiling_class.Get() == nullptr) {
+ // We could not determine the method's class we need to wait until runtime.
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ return nullptr;
+ }
+ ArtMethod* current_method = graph_->GetArtMethod();
+ DCHECK(current_method != nullptr);
+ Handle<mirror::Class> methods_class(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
+ method_idx,
+ current_method)));
+ if (methods_class.Get() == nullptr) {
+ // Invoking a super method requires knowing the actual super class. If we did not resolve
+ // the compiling method's declaring class (which only happens for ahead of time
+ // compilation), bail out.
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ return nullptr;
+ } else {
+ ArtMethod* actual_method;
+ if (methods_class->IsInterface()) {
+ actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+ resolved_method, class_linker->GetImagePointerSize());
+ } else {
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
+ vtable_index, class_linker->GetImagePointerSize());
+ }
+ if (actual_method != resolved_method &&
+ !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ // The back-end code generator relies on this check in order to ensure that it will not
+ // attempt to read the dex_cache with a dex_method_index that is not from the correct
+ // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
+ // builder, which means that the code-generator (and compiler driver during sharpening and
+ // inliner, maybe) might invoke an incorrect method.
+ // TODO: The actual method could still be referenced in the current dex file, so we
+ // could try locating it.
+ // TODO: Remove the dex_file restriction.
+ return nullptr;
+ }
+ if (!actual_method->IsInvokable()) {
+ // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
+ // could resolve the callee to the wrong method.
+ return nullptr;
+ }
+ resolved_method = actual_method;
+ }
+ }
+
+ // Check for incompatible class changes. The class linker has a fast path for
+ // looking into the dex cache and does not check incompatible class changes if it hits it.
+ if (resolved_method->CheckIncompatibleClassChange(invoke_type)) {
+ return nullptr;
+ }
+
+ return resolved_method;
+}
+
+bool HInstructionBuilder::BuildInvoke(const Instruction& instruction,
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index) {
+ InvokeType invoke_type = GetInvokeTypeFromOpCode(instruction.Opcode());
+ const char* descriptor = dex_file_->GetMethodShorty(method_idx);
+ Primitive::Type return_type = Primitive::GetType(descriptor[0]);
+
+ // Remove the return type from the 'proto'.
+ size_t number_of_arguments = strlen(descriptor) - 1;
+ if (invoke_type != kStatic) { // instance call
+ // One extra argument for 'this'.
+ number_of_arguments++;
+ }
+
+ MethodReference target_method(dex_file_, method_idx);
+
+ // Special handling for string init.
+ int32_t string_init_offset = 0;
+ bool is_string_init = compiler_driver_->IsStringInit(method_idx,
+ dex_file_,
+ &string_init_offset);
+ // Replace calls to String.<init> with StringFactory.
+ if (is_string_init) {
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ dchecked_integral_cast<uint64_t>(string_init_offset),
+ 0U
+ };
+ HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
+ arena_,
+ number_of_arguments - 1,
+ Primitive::kPrimNot /*return_type */,
+ dex_pc,
+ method_idx,
+ target_method,
+ dispatch_info,
+ invoke_type,
+ kStatic /* optimized_invoke_type */,
+ HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit);
+ return HandleStringInit(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor);
+ }
+
+ ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
+
+ if (UNLIKELY(resolved_method == nullptr)) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
+ HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ invoke_type);
+ return HandleInvoke(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ nullptr /* clinit_check */);
+ }
+
+ // Potential class initialization check, in the case of a static method call.
+ HClinitCheck* clinit_check = nullptr;
+ HInvoke* invoke = nullptr;
+ if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) {
+ // By default, consider that the called method implicitly requires
+ // an initialization check of its declaring method.
+ HInvokeStaticOrDirect::ClinitCheckRequirement clinit_check_requirement
+ = HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit;
+ ScopedObjectAccess soa(Thread::Current());
+ if (invoke_type == kStatic) {
+ clinit_check = ProcessClinitCheckForInvoke(
+ dex_pc, resolved_method, method_idx, &clinit_check_requirement);
+ } else if (invoke_type == kSuper) {
+ if (IsSameDexFile(*resolved_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ // Update the target method to the one resolved. Note that this may be a no-op if
+ // we resolved to the method referenced by the instruction.
+ method_idx = resolved_method->GetDexMethodIndex();
+ target_method = MethodReference(dex_file_, method_idx);
+ }
+ }
+
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0U
+ };
+ invoke = new (arena_) HInvokeStaticOrDirect(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ target_method,
+ dispatch_info,
+ invoke_type,
+ invoke_type,
+ clinit_check_requirement);
+ } else if (invoke_type == kVirtual) {
+ ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
+ invoke = new (arena_) HInvokeVirtual(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ resolved_method->GetMethodIndex());
+ } else {
+ DCHECK_EQ(invoke_type, kInterface);
+ ScopedObjectAccess soa(Thread::Current()); // Needed for the method index
+ invoke = new (arena_) HInvokeInterface(arena_,
+ number_of_arguments,
+ return_type,
+ dex_pc,
+ method_idx,
+ resolved_method->GetDexMethodIndex());
+ }
+
+ return HandleInvoke(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ clinit_check);
+}
+
+bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) {
+ bool finalizable;
+ bool can_throw = NeedsAccessCheck(type_index, &finalizable);
+
+ // Only the non-resolved entrypoint handles the finalizable class case. If we
+ // need access checks, then we haven't resolved the method and the class may
+ // again be finalizable.
+ QuickEntrypointEnum entrypoint = (finalizable || can_throw)
+ ? kQuickAllocObject
+ : kQuickAllocObjectInitialized;
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
+
+ if (outer_dex_cache.Get() != dex_cache.Get()) {
+ // We currently do not support inlining allocations across dex files.
+ return false;
+ }
+
+ HLoadClass* load_class = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ outer_dex_file,
+ IsOutermostCompilingClass(type_index),
+ dex_pc,
+ /*needs_access_check*/ can_throw,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index));
+
+ AppendInstruction(load_class);
+ HInstruction* cls = load_class;
+ if (!IsInitialized(resolved_class)) {
+ cls = new (arena_) HClinitCheck(load_class, dex_pc);
+ AppendInstruction(cls);
+ }
+
+ AppendInstruction(new (arena_) HNewInstance(
+ cls,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ can_throw,
+ finalizable,
+ entrypoint));
+ return true;
+}
+
+static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class);
+}
+
+bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const {
+ if (cls.Get() == nullptr) {
+ return false;
+ }
+
+ // `CanAssumeClassIsLoaded` will return true if we're JITting, or will
+ // check whether the class is in an image for the AOT compilation.
+ if (cls->IsInitialized() &&
+ compiler_driver_->CanAssumeClassIsLoaded(cls.Get())) {
+ return true;
+ }
+
+ if (IsSubClass(GetOutermostCompilingClass(), cls.Get())) {
+ return true;
+ }
+
+ // TODO: We should walk over the inlined methods, but we don't pass
+ // that information to the builder.
+ if (IsSubClass(GetCompilingClass(), cls.Get())) {
+ return true;
+ }
+
+ return false;
+}
+
+HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke(
+ uint32_t dex_pc,
+ ArtMethod* resolved_method,
+ uint32_t method_idx,
+ HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) {
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Thread* self = Thread::Current();
+ StackHandleScope<4> hs(self);
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ self, *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(
+ self, outer_dex_file)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+ Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass()));
+
+ // The index at which the method's class is stored in the DexCache's type array.
+ uint32_t storage_index = DexFile::kDexNoIndex;
+ bool is_outer_class = (resolved_method->GetDeclaringClass() == outer_class.Get());
+ if (is_outer_class) {
+ storage_index = outer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() == dex_cache.Get()) {
+ // Get `storage_index` from IsClassOfStaticMethodAvailableToReferrer.
+ compiler_driver_->IsClassOfStaticMethodAvailableToReferrer(outer_dex_cache.Get(),
+ GetCompilingClass(),
+ resolved_method,
+ method_idx,
+ &storage_index);
+ }
+
+ HClinitCheck* clinit_check = nullptr;
+
+ if (IsInitialized(resolved_method_class)) {
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
+ } else if (storage_index != DexFile::kDexNoIndex) {
+ *clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit;
+ HLoadClass* load_class = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ storage_index,
+ outer_dex_file,
+ is_outer_class,
+ dex_pc,
+ /*needs_access_check*/ false,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index));
+ AppendInstruction(load_class);
+ clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
+ AppendInstruction(clinit_check);
+ }
+ return clinit_check;
+}
+
+bool HInstructionBuilder::SetupInvokeArguments(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ size_t start_index,
+ size_t* argument_index) {
+ uint32_t descriptor_index = 1; // Skip the return type.
+
+ for (size_t i = start_index;
+ // Make sure we don't go over the expected arguments or over the number of
+ // dex registers given. If the instruction was seen as dead by the verifier,
+ // it hasn't been properly checked.
+ (i < number_of_vreg_arguments) && (*argument_index < invoke->GetNumberOfArguments());
+ i++, (*argument_index)++) {
+ Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]);
+ bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble);
+ if (!is_range
+ && is_wide
+ && ((i + 1 == number_of_vreg_arguments) || (args[i] + 1 != args[i + 1]))) {
+ // Longs and doubles should be in pairs, that is, sequential registers. The verifier should
+ // reject any class where this is violated. However, the verifier only does these checks
+ // on non trivially dead instructions, so we just bailout the compilation.
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of non-sequential dex register pair in wide argument";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
+ return false;
+ }
+ HInstruction* arg = LoadLocal(is_range ? register_index + i : args[i], type);
+ invoke->SetArgumentAt(*argument_index, arg);
+ if (is_wide) {
+ i++;
+ }
+ }
+
+ if (*argument_index != invoke->GetNumberOfArguments()) {
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of wrong number of arguments in invoke instruction";
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledMalformedOpcode);
+ return false;
+ }
+
+ if (invoke->IsInvokeStaticOrDirect() &&
+ HInvokeStaticOrDirect::NeedsCurrentMethodInput(
+ invoke->AsInvokeStaticOrDirect()->GetMethodLoadKind())) {
+ invoke->SetArgumentAt(*argument_index, graph_->GetCurrentMethod());
+ (*argument_index)++;
+ }
+
+ return true;
+}
+
+bool HInstructionBuilder::HandleInvoke(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ HClinitCheck* clinit_check) {
+ DCHECK(!invoke->IsInvokeStaticOrDirect() || !invoke->AsInvokeStaticOrDirect()->IsStringInit());
+
+ size_t start_index = 0;
+ size_t argument_index = 0;
+ if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) { // Instance call.
+ HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
+ HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
+ AppendInstruction(null_check);
+ invoke->SetArgumentAt(0, null_check);
+ start_index = 1;
+ argument_index = 1;
+ }
+
+ if (!SetupInvokeArguments(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ start_index,
+ &argument_index)) {
+ return false;
+ }
+
+ if (clinit_check != nullptr) {
+ // Add the class initialization check as last input of `invoke`.
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ DCHECK(invoke->AsInvokeStaticOrDirect()->GetClinitCheckRequirement()
+ == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit);
+ invoke->SetArgumentAt(argument_index, clinit_check);
+ argument_index++;
+ }
+
+ AppendInstruction(invoke);
+ latest_result_ = invoke;
+
+ return true;
+}
+
+bool HInstructionBuilder::HandleStringInit(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ DCHECK(invoke->AsInvokeStaticOrDirect()->IsStringInit());
+
+ size_t start_index = 1;
+ size_t argument_index = 0;
+ if (!SetupInvokeArguments(invoke,
+ number_of_vreg_arguments,
+ args,
+ register_index,
+ is_range,
+ descriptor,
+ start_index,
+ &argument_index)) {
+ return false;
+ }
+
+ AppendInstruction(invoke);
+
+ // This is a StringFactory call, not an actual String constructor. Its result
+ // replaces the empty String pre-allocated by NewInstance.
+ uint32_t orig_this_reg = is_range ? register_index : args[0];
+ HInstruction* arg_this = LoadLocal(orig_this_reg, Primitive::kPrimNot);
+
+ // Replacing the NewInstance might render it redundant. Keep a list of these
+ // to be visited once it is clear whether it is has remaining uses.
+ if (arg_this->IsNewInstance()) {
+ ssa_builder_->AddUninitializedString(arg_this->AsNewInstance());
+ } else {
+ DCHECK(arg_this->IsPhi());
+ // NewInstance is not the direct input of the StringFactory call. It might
+ // be redundant but optimizing this case is not worth the effort.
+ }
+
+ // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`.
+ for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
+ if ((*current_locals_)[vreg] == arg_this) {
+ (*current_locals_)[vreg] = invoke;
+ }
+ }
+
+ return true;
+}
+
+static Primitive::Type GetFieldAccessType(const DexFile& dex_file, uint16_t field_index) {
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ return Primitive::GetType(type[0]);
+}
+
+bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put) {
+ uint32_t source_or_dest_reg = instruction.VRegA_22c();
+ uint32_t obj_reg = instruction.VRegB_22c();
+ uint16_t field_index;
+ if (instruction.IsQuickened()) {
+ if (!CanDecodeQuickenedInfo()) {
+ return false;
+ }
+ field_index = LookupQuickenedInfo(dex_pc);
+ } else {
+ field_index = instruction.VRegC_22c();
+ }
+
+ ScopedObjectAccess soa(Thread::Current());
+ ArtField* resolved_field =
+ compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
+
+
+ HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
+ HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
+ AppendInstruction(null_check);
+
+ Primitive::Type field_type = (resolved_field == nullptr)
+ ? GetFieldAccessType(*dex_file_, field_index)
+ : resolved_field->GetTypeAsPrimitiveType();
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+ HInstruction* field_set = nullptr;
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
+ value,
+ field_type,
+ field_index,
+ dex_pc);
+ } else {
+ uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
+ field_set = new (arena_) HInstanceFieldSet(null_check,
+ value,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc);
+ }
+ AppendInstruction(field_set);
+ } else {
+ HInstruction* field_get = nullptr;
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
+ field_type,
+ field_index,
+ dex_pc);
+ } else {
+ uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
+ field_get = new (arena_) HInstanceFieldGet(null_check,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_compilation_unit_->GetDexCache(),
+ dex_pc);
+ }
+ AppendInstruction(field_get);
+ UpdateLocal(source_or_dest_reg, field_get);
+ }
+
+ return true;
+}
+
+static mirror::Class* GetClassFrom(CompilerDriver* driver,
+ const DexCompilationUnit& compilation_unit) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ const DexFile& dex_file = *compilation_unit.GetDexFile();
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader())));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
+
+ return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
+}
+
+mirror::Class* HInstructionBuilder::GetOutermostCompilingClass() const {
+ return GetClassFrom(compiler_driver_, *outer_compilation_unit_);
+}
+
+mirror::Class* HInstructionBuilder::GetCompilingClass() const {
+ return GetClassFrom(compiler_driver_, *dex_compilation_unit_);
+}
+
+bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
+ soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ // GetOutermostCompilingClass returns null when the class is unresolved
+ // (e.g. if it derives from an unresolved class). This is bogus knowing that
+ // we are compiling it.
+ // When this happens we cannot establish a direct relation between the current
+ // class and the outer class, so we return false.
+ // (Note that this is only used for optimizing invokes and field accesses)
+ return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get());
+}
+
+void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type) {
+ uint32_t source_or_dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+ AppendInstruction(
+ new (arena_) HUnresolvedStaticFieldSet(value, field_type, field_index, dex_pc));
+ } else {
+ AppendInstruction(new (arena_) HUnresolvedStaticFieldGet(field_type, field_index, dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ }
+}
+
+bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put) {
+ uint32_t source_or_dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<5> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(
+ soa.Self(), *dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ ArtField* resolved_field = compiler_driver_->ResolveField(
+ soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true);
+
+ if (resolved_field == nullptr) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
+ Primitive::Type field_type = GetFieldAccessType(*dex_file_, field_index);
+ BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+ return true;
+ }
+
+ Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file)));
+ Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ // The index at which the field's class is stored in the DexCache's type array.
+ uint32_t storage_index;
+ bool is_outer_class = (outer_class.Get() == resolved_field->GetDeclaringClass());
+ if (is_outer_class) {
+ storage_index = outer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() != dex_cache.Get()) {
+ // The compiler driver cannot currently understand multiple dex caches involved. Just bailout.
+ return false;
+ } else {
+ // TODO: This is rather expensive. Perf it and cache the results if needed.
+ std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+ outer_dex_cache.Get(),
+ GetCompilingClass(),
+ resolved_field,
+ field_index,
+ &storage_index);
+ bool can_easily_access = is_put ? pair.second : pair.first;
+ if (!can_easily_access) {
+ MaybeRecordStat(MethodCompilationStat::kUnresolvedFieldNotAFastAccess);
+ BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type);
+ return true;
+ }
+ }
+
+ bool is_in_cache =
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index);
+ HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(),
+ storage_index,
+ outer_dex_file,
+ is_outer_class,
+ dex_pc,
+ /*needs_access_check*/ false,
+ is_in_cache);
+ AppendInstruction(constant);
+
+ HInstruction* cls = constant;
+
+ Handle<mirror::Class> klass(hs.NewHandle(resolved_field->GetDeclaringClass()));
+ if (!IsInitialized(klass)) {
+ cls = new (arena_) HClinitCheck(constant, dex_pc);
+ AppendInstruction(cls);
+ }
+
+ uint16_t class_def_index = klass->GetDexClassDefIndex();
+ if (is_put) {
+ // We need to keep the class alive before loading the value.
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+ DCHECK_EQ(HPhi::ToPhiType(value->GetType()), HPhi::ToPhiType(field_type));
+ AppendInstruction(new (arena_) HStaticFieldSet(cls,
+ value,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_cache_,
+ dex_pc));
+ } else {
+ AppendInstruction(new (arena_) HStaticFieldGet(cls,
+ field_type,
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ class_def_index,
+ *dex_file_,
+ dex_cache_,
+ dex_pc));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ }
+ return true;
+}
+
+void HInstructionBuilder::BuildCheckedDivRem(uint16_t out_vreg,
+ uint16_t first_vreg,
+ int64_t second_vreg_or_constant,
+ uint32_t dex_pc,
+ Primitive::Type type,
+ bool second_is_constant,
+ bool isDiv) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ HInstruction* first = LoadLocal(first_vreg, type);
+ HInstruction* second = nullptr;
+ if (second_is_constant) {
+ if (type == Primitive::kPrimInt) {
+ second = graph_->GetIntConstant(second_vreg_or_constant, dex_pc);
+ } else {
+ second = graph_->GetLongConstant(second_vreg_or_constant, dex_pc);
+ }
+ } else {
+ second = LoadLocal(second_vreg_or_constant, type);
+ }
+
+ if (!second_is_constant
+ || (type == Primitive::kPrimInt && second->AsIntConstant()->GetValue() == 0)
+ || (type == Primitive::kPrimLong && second->AsLongConstant()->GetValue() == 0)) {
+ second = new (arena_) HDivZeroCheck(second, dex_pc);
+ AppendInstruction(second);
+ }
+
+ if (isDiv) {
+ AppendInstruction(new (arena_) HDiv(type, first, second, dex_pc));
+ } else {
+ AppendInstruction(new (arena_) HRem(type, first, second, dex_pc));
+ }
+ UpdateLocal(out_vreg, current_block_->GetLastInstruction());
+}
+
+void HInstructionBuilder::BuildArrayAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type anticipated_type) {
+ uint8_t source_or_dest_reg = instruction.VRegA_23x();
+ uint8_t array_reg = instruction.VRegB_23x();
+ uint8_t index_reg = instruction.VRegC_23x();
+
+ HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
+ object = new (arena_) HNullCheck(object, dex_pc);
+ AppendInstruction(object);
+
+ HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
+ AppendInstruction(length);
+ HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
+ index = new (arena_) HBoundsCheck(index, length, dex_pc);
+ AppendInstruction(index);
+ if (is_put) {
+ HInstruction* value = LoadLocal(source_or_dest_reg, anticipated_type);
+ // TODO: Insert a type check node if the type is Object.
+ HArraySet* aset = new (arena_) HArraySet(object, index, value, anticipated_type, dex_pc);
+ ssa_builder_->MaybeAddAmbiguousArraySet(aset);
+ AppendInstruction(aset);
+ } else {
+ HArrayGet* aget = new (arena_) HArrayGet(object, index, anticipated_type, dex_pc);
+ ssa_builder_->MaybeAddAmbiguousArrayGet(aget);
+ AppendInstruction(aget);
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ }
+ graph_->SetHasBoundsChecks(true);
+}
+
+void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc,
+ uint32_t type_index,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index) {
+ HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
+ bool finalizable;
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
+ HInstruction* object = new (arena_) HNewArray(length,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint);
+ AppendInstruction(object);
+
+ const char* descriptor = dex_file_->StringByTypeIdx(type_index);
+ DCHECK_EQ(descriptor[0], '[') << descriptor;
+ char primitive = descriptor[1];
+ DCHECK(primitive == 'I'
+ || primitive == 'L'
+ || primitive == '[') << descriptor;
+ bool is_reference_array = (primitive == 'L') || (primitive == '[');
+ Primitive::Type type = is_reference_array ? Primitive::kPrimNot : Primitive::kPrimInt;
+
+ for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
+ HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HArraySet* aset = new (arena_) HArraySet(object, index, value, type, dex_pc);
+ ssa_builder_->MaybeAddAmbiguousArraySet(aset);
+ AppendInstruction(aset);
+ }
+ latest_result_ = object;
+}
+
+template <typename T>
+void HInstructionBuilder::BuildFillArrayData(HInstruction* object,
+ const T* data,
+ uint32_t element_count,
+ Primitive::Type anticipated_type,
+ uint32_t dex_pc) {
+ for (uint32_t i = 0; i < element_count; ++i) {
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HInstruction* value = graph_->GetIntConstant(data[i], dex_pc);
+ HArraySet* aset = new (arena_) HArraySet(object, index, value, anticipated_type, dex_pc);
+ ssa_builder_->MaybeAddAmbiguousArraySet(aset);
+ AppendInstruction(aset);
+ }
+}
+
+void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
+ HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
+ AppendInstruction(null_check);
+
+ HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
+ AppendInstruction(length);
+
+ int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item_.insns_ + payload_offset);
+ const uint8_t* data = payload->data;
+ uint32_t element_count = payload->element_count;
+
+ // Implementation of this DEX instruction seems to be that the bounds check is
+ // done before doing any stores.
+ HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1, dex_pc);
+ AppendInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
+
+ switch (payload->element_width) {
+ case 1:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const int8_t*>(data),
+ element_count,
+ Primitive::kPrimByte,
+ dex_pc);
+ break;
+ case 2:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const int16_t*>(data),
+ element_count,
+ Primitive::kPrimShort,
+ dex_pc);
+ break;
+ case 4:
+ BuildFillArrayData(null_check,
+ reinterpret_cast<const int32_t*>(data),
+ element_count,
+ Primitive::kPrimInt,
+ dex_pc);
+ break;
+ case 8:
+ BuildFillWideArrayData(null_check,
+ reinterpret_cast<const int64_t*>(data),
+ element_count,
+ dex_pc);
+ break;
+ default:
+ LOG(FATAL) << "Unknown element width for " << payload->element_width;
+ }
+ graph_->SetHasBoundsChecks(true);
+}
+
+void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object,
+ const int64_t* data,
+ uint32_t element_count,
+ uint32_t dex_pc) {
+ for (uint32_t i = 0; i < element_count; ++i) {
+ HInstruction* index = graph_->GetIntConstant(i, dex_pc);
+ HInstruction* value = graph_->GetLongConstant(data[i], dex_pc);
+ HArraySet* aset = new (arena_) HArraySet(object, index, value, Primitive::kPrimLong, dex_pc);
+ ssa_builder_->MaybeAddAmbiguousArraySet(aset);
+ AppendInstruction(aset);
+ }
+}
+
+static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (cls.Get() == nullptr) {
+ return TypeCheckKind::kUnresolvedCheck;
+ } else if (cls->IsInterface()) {
+ return TypeCheckKind::kInterfaceCheck;
+ } else if (cls->IsArrayClass()) {
+ if (cls->GetComponentType()->IsObjectClass()) {
+ return TypeCheckKind::kArrayObjectCheck;
+ } else if (cls->CannotBeAssignedFromOtherTypes()) {
+ return TypeCheckKind::kExactCheck;
+ } else {
+ return TypeCheckKind::kArrayCheck;
+ }
+ } else if (cls->IsFinal()) {
+ return TypeCheckKind::kExactCheck;
+ } else if (cls->IsAbstract()) {
+ return TypeCheckKind::kAbstractClassCheck;
+ } else {
+ return TypeCheckKind::kClassHierarchyCheck;
+ }
+}
+
+void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
+ uint8_t destination,
+ uint8_t reference,
+ uint16_t type_index,
+ uint32_t dex_pc) {
+ bool type_known_final, type_known_abstract, use_declaring_class;
+ bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(),
+ *dex_compilation_unit_->GetDexFile(),
+ type_index,
+ &type_known_final,
+ &type_known_abstract,
+ &use_declaring_class);
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file)));
+ Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index)));
+
+ HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
+ HLoadClass* cls = new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ dex_file,
+ IsOutermostCompilingClass(type_index),
+ dex_pc,
+ !can_access,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index));
+ AppendInstruction(cls);
+
+ TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
+ if (instruction.Opcode() == Instruction::INSTANCE_OF) {
+ AppendInstruction(new (arena_) HInstanceOf(object, cls, check_kind, dex_pc));
+ UpdateLocal(destination, current_block_->GetLastInstruction());
+ } else {
+ DCHECK_EQ(instruction.Opcode(), Instruction::CHECK_CAST);
+ // We emit a CheckCast followed by a BoundType. CheckCast is a statement
+ // which may throw. If it succeeds BoundType sets the new type of `object`
+ // for all subsequent uses.
+ AppendInstruction(new (arena_) HCheckCast(object, cls, check_kind, dex_pc));
+ AppendInstruction(new (arena_) HBoundType(object, dex_pc));
+ UpdateLocal(reference, current_block_->GetLastInstruction());
+ }
+}
+
+bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const {
+ return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable);
+}
+
+bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
+ return interpreter_metadata_ != nullptr;
+}
+
+uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
+ DCHECK(interpreter_metadata_ != nullptr);
+ uint32_t dex_pc_in_map = DecodeUnsignedLeb128(&interpreter_metadata_);
+ DCHECK_EQ(dex_pc, dex_pc_in_map);
+ return DecodeUnsignedLeb128(&interpreter_metadata_);
+}
+
+bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc) {
+ switch (instruction.Opcode()) {
+ case Instruction::CONST_4: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n(), dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST_16: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s(), dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i(), dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16, dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_16: {
+ int32_t register_index = instruction.VRegA();
+ // Get 16 bits of constant value, sign extended to 64 bits.
+ int64_t value = instruction.VRegB_21s();
+ value <<= 48;
+ value >>= 48;
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_32: {
+ int32_t register_index = instruction.VRegA();
+ // Get 32 bits of constant value, sign extended to 64 bits.
+ int64_t value = instruction.VRegB_31i();
+ value <<= 32;
+ value >>= 32;
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST_WIDE: {
+ int32_t register_index = instruction.VRegA();
+ HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l(), dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ case Instruction::CONST_WIDE_HIGH16: {
+ int32_t register_index = instruction.VRegA();
+ int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
+ HLongConstant* constant = graph_->GetLongConstant(value, dex_pc);
+ UpdateLocal(register_index, constant);
+ break;
+ }
+
+ // Note that the SSA building will refine the types.
+ case Instruction::MOVE:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_16: {
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
+ UpdateLocal(instruction.VRegA(), value);
+ break;
+ }
+
+ // Note that the SSA building will refine the types.
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_FROM16:
+ case Instruction::MOVE_WIDE_16: {
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimLong);
+ UpdateLocal(instruction.VRegA(), value);
+ break;
+ }
+
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_OBJECT_16:
+ case Instruction::MOVE_OBJECT_FROM16: {
+ HInstruction* value = LoadLocal(instruction.VRegB(), Primitive::kPrimNot);
+ UpdateLocal(instruction.VRegA(), value);
+ break;
+ }
+
+ case Instruction::RETURN_VOID_NO_BARRIER:
+ case Instruction::RETURN_VOID: {
+ BuildReturn(instruction, Primitive::kPrimVoid, dex_pc);
+ break;
+ }
+
+#define IF_XX(comparison, cond) \
+ case Instruction::IF_##cond: If_22t<comparison>(instruction, dex_pc); break; \
+ case Instruction::IF_##cond##Z: If_21t<comparison>(instruction, dex_pc); break
+
+ IF_XX(HEqual, EQ);
+ IF_XX(HNotEqual, NE);
+ IF_XX(HLessThan, LT);
+ IF_XX(HLessThanOrEqual, LE);
+ IF_XX(HGreaterThan, GT);
+ IF_XX(HGreaterThanOrEqual, GE);
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32: {
+ AppendInstruction(new (arena_) HGoto(dex_pc));
+ current_block_ = nullptr;
+ break;
+ }
+
+ case Instruction::RETURN: {
+ BuildReturn(instruction, return_type_, dex_pc);
+ break;
+ }
+
+ case Instruction::RETURN_OBJECT: {
+ BuildReturn(instruction, return_type_, dex_pc);
+ break;
+ }
+
+ case Instruction::RETURN_WIDE: {
+ BuildReturn(instruction, return_type_, dex_pc);
+ break;
+ }
+
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_QUICK: {
+ uint16_t method_idx;
+ if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) {
+ if (!CanDecodeQuickenedInfo()) {
+ return false;
+ }
+ method_idx = LookupQuickenedInfo(dex_pc);
+ } else {
+ method_idx = instruction.VRegB_35c();
+ }
+ uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
+ uint32_t args[5];
+ instruction.GetVarArgs(args);
+ if (!BuildInvoke(instruction, dex_pc, method_idx,
+ number_of_vreg_arguments, false, args, -1)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ uint16_t method_idx;
+ if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) {
+ if (!CanDecodeQuickenedInfo()) {
+ return false;
+ }
+ method_idx = LookupQuickenedInfo(dex_pc);
+ } else {
+ method_idx = instruction.VRegB_3rc();
+ }
+ uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
+ uint32_t register_index = instruction.VRegC();
+ if (!BuildInvoke(instruction, dex_pc, method_idx,
+ number_of_vreg_arguments, true, nullptr, register_index)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::NEG_INT: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::NEG_LONG: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::NEG_FLOAT: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::NEG_DOUBLE: {
+ Unop_12x<HNeg>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::NOT_INT: {
+ Unop_12x<HNot>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::NOT_LONG: {
+ Unop_12x<HNot>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::LONG_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::LONG_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::LONG_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::FLOAT_TO_DOUBLE: {
+ Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_INT: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_LONG: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::DOUBLE_TO_FLOAT: {
+ Conversion_12x(instruction, Primitive::kPrimDouble, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_BYTE: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_SHORT: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort, dex_pc);
+ break;
+ }
+
+ case Instruction::INT_TO_CHAR: {
+ Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_LONG: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_DOUBLE: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_FLOAT: {
+ Binop_23x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_INT: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_LONG: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_FLOAT: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_DOUBLE: {
+ Binop_23x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_LONG: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_FLOAT: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_DOUBLE: {
+ Binop_23x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_INT: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, false, true);
+ break;
+ }
+
+ case Instruction::DIV_LONG: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimLong, false, true);
+ break;
+ }
+
+ case Instruction::DIV_FLOAT: {
+ Binop_23x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_DOUBLE: {
+ Binop_23x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_INT: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, false, false);
+ break;
+ }
+
+ case Instruction::REM_LONG: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimLong, false, false);
+ break;
+ }
+
+ case Instruction::REM_FLOAT: {
+ Binop_23x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_DOUBLE: {
+ Binop_23x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT: {
+ Binop_23x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_LONG: {
+ Binop_23x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_INT: {
+ Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_LONG: {
+ Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_INT: {
+ Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_LONG: {
+ Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_INT: {
+ Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_LONG: {
+ Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT: {
+ Binop_23x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_LONG: {
+ Binop_23x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT: {
+ Binop_23x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_LONG: {
+ Binop_23x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_LONG_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_DOUBLE_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_FLOAT_2ADDR: {
+ Binop_12x<HAdd>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_INT_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_LONG_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_FLOAT_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::SUB_DOUBLE_2ADDR: {
+ Binop_12x<HSub>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_LONG_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_FLOAT_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_DOUBLE_2ADDR: {
+ Binop_12x<HMul>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_INT_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimInt, false, true);
+ break;
+ }
+
+ case Instruction::DIV_LONG_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimLong, false, true);
+ break;
+ }
+
+ case Instruction::REM_INT_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimInt, false, false);
+ break;
+ }
+
+ case Instruction::REM_LONG_2ADDR: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(),
+ dex_pc, Primitive::kPrimLong, false, false);
+ break;
+ }
+
+ case Instruction::REM_FLOAT_2ADDR: {
+ Binop_12x<HRem>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::REM_DOUBLE_2ADDR: {
+ Binop_12x<HRem>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_INT_2ADDR: {
+ Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHL_LONG_2ADDR: {
+ Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_INT_2ADDR: {
+ Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_LONG_2ADDR: {
+ Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_INT_2ADDR: {
+ Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_LONG_2ADDR: {
+ Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_FLOAT_2ADDR: {
+ Binop_12x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_DOUBLE_2ADDR: {
+ Binop_12x<HDiv>(instruction, Primitive::kPrimDouble, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT_2ADDR: {
+ Binop_12x<HAnd>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_LONG_2ADDR: {
+ Binop_12x<HAnd>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT_2ADDR: {
+ Binop_12x<HOr>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_LONG_2ADDR: {
+ Binop_12x<HOr>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT_2ADDR: {
+ Binop_12x<HXor>(instruction, Primitive::kPrimInt, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_LONG_2ADDR: {
+ Binop_12x<HXor>(instruction, Primitive::kPrimLong, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT16: {
+ Binop_22s<HAdd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT_LIT16: {
+ Binop_22s<HAnd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT_LIT16: {
+ Binop_22s<HOr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT_LIT16: {
+ Binop_22s<HXor>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::RSUB_INT: {
+ Binop_22s<HSub>(instruction, true, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT_LIT16: {
+ Binop_22s<HMul>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::ADD_INT_LIT8: {
+ Binop_22b<HAdd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::AND_INT_LIT8: {
+ Binop_22b<HAnd>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::OR_INT_LIT8: {
+ Binop_22b<HOr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::XOR_INT_LIT8: {
+ Binop_22b<HXor>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::RSUB_INT_LIT8: {
+ Binop_22b<HSub>(instruction, true, dex_pc);
+ break;
+ }
+
+ case Instruction::MUL_INT_LIT8: {
+ Binop_22b<HMul>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::DIV_INT_LIT8: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, true, true);
+ break;
+ }
+
+ case Instruction::REM_INT_LIT16:
+ case Instruction::REM_INT_LIT8: {
+ BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(),
+ dex_pc, Primitive::kPrimInt, true, false);
+ break;
+ }
+
+ case Instruction::SHL_INT_LIT8: {
+ Binop_22b<HShl>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::SHR_INT_LIT8: {
+ Binop_22b<HShr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::USHR_INT_LIT8: {
+ Binop_22b<HUShr>(instruction, false, dex_pc);
+ break;
+ }
+
+ case Instruction::NEW_INSTANCE: {
+ if (!BuildNewInstance(instruction.VRegB_21c(), dex_pc)) {
+ return false;
+ }
+ UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction());
+ break;
+ }
+
+ case Instruction::NEW_ARRAY: {
+ uint16_t type_index = instruction.VRegC_22c();
+ HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
+ bool finalizable;
+ QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
+ ? kQuickAllocArrayWithAccessCheck
+ : kQuickAllocArray;
+ AppendInstruction(new (arena_) HNewArray(length,
+ graph_->GetCurrentMethod(),
+ dex_pc,
+ type_index,
+ *dex_compilation_unit_->GetDexFile(),
+ entrypoint));
+ UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY: {
+ uint32_t number_of_vreg_arguments = instruction.VRegA_35c();
+ uint32_t type_index = instruction.VRegB_35c();
+ uint32_t args[5];
+ instruction.GetVarArgs(args);
+ BuildFilledNewArray(dex_pc, type_index, number_of_vreg_arguments, false, args, 0);
+ break;
+ }
+
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ uint32_t number_of_vreg_arguments = instruction.VRegA_3rc();
+ uint32_t type_index = instruction.VRegB_3rc();
+ uint32_t register_index = instruction.VRegC_3rc();
+ BuildFilledNewArray(
+ dex_pc, type_index, number_of_vreg_arguments, true, nullptr, register_index);
+ break;
+ }
+
+ case Instruction::FILL_ARRAY_DATA: {
+ BuildFillArrayData(instruction, dex_pc);
+ break;
+ }
+
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_WIDE:
+ case Instruction::MOVE_RESULT_OBJECT: {
+ DCHECK(latest_result_ != nullptr);
+ UpdateLocal(instruction.VRegA(), latest_result_);
+ latest_result_ = nullptr;
+ break;
+ }
+
+ case Instruction::CMP_LONG: {
+ Binop_23x_cmp(instruction, Primitive::kPrimLong, ComparisonBias::kNoBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPG_FLOAT: {
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kGtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPG_DOUBLE: {
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kGtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPL_FLOAT: {
+ Binop_23x_cmp(instruction, Primitive::kPrimFloat, ComparisonBias::kLtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::CMPL_DOUBLE: {
+ Binop_23x_cmp(instruction, Primitive::kPrimDouble, ComparisonBias::kLtBias, dex_pc);
+ break;
+ }
+
+ case Instruction::NOP:
+ break;
+
+ case Instruction::IGET:
+ case Instruction::IGET_QUICK:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_WIDE_QUICK:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IGET_OBJECT_QUICK:
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BOOLEAN_QUICK:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_BYTE_QUICK:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_CHAR_QUICK:
+ case Instruction::IGET_SHORT:
+ case Instruction::IGET_SHORT_QUICK: {
+ if (!BuildInstanceFieldAccess(instruction, dex_pc, false)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::IPUT:
+ case Instruction::IPUT_QUICK:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_WIDE_QUICK:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_OBJECT_QUICK:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BOOLEAN_QUICK:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_BYTE_QUICK:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_CHAR_QUICK:
+ case Instruction::IPUT_SHORT:
+ case Instruction::IPUT_SHORT_QUICK: {
+ if (!BuildInstanceFieldAccess(instruction, dex_pc, true)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::SGET:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT: {
+ if (!BuildStaticFieldAccess(instruction, dex_pc, false)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT: {
+ if (!BuildStaticFieldAccess(instruction, dex_pc, true)) {
+ return false;
+ }
+ break;
+ }
+
+#define ARRAY_XX(kind, anticipated_type) \
+ case Instruction::AGET##kind: { \
+ BuildArrayAccess(instruction, dex_pc, false, anticipated_type); \
+ break; \
+ } \
+ case Instruction::APUT##kind: { \
+ BuildArrayAccess(instruction, dex_pc, true, anticipated_type); \
+ break; \
+ }
+
+ ARRAY_XX(, Primitive::kPrimInt);
+ ARRAY_XX(_WIDE, Primitive::kPrimLong);
+ ARRAY_XX(_OBJECT, Primitive::kPrimNot);
+ ARRAY_XX(_BOOLEAN, Primitive::kPrimBoolean);
+ ARRAY_XX(_BYTE, Primitive::kPrimByte);
+ ARRAY_XX(_CHAR, Primitive::kPrimChar);
+ ARRAY_XX(_SHORT, Primitive::kPrimShort);
+
+ case Instruction::ARRAY_LENGTH: {
+ HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
+ object = new (arena_) HNullCheck(object, dex_pc);
+ AppendInstruction(object);
+ AppendInstruction(new (arena_) HArrayLength(object, dex_pc));
+ UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
+ break;
+ }
+
+ case Instruction::CONST_STRING: {
+ uint32_t string_index = instruction.VRegB_21c();
+ AppendInstruction(
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
+ UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+ break;
+ }
+
+ case Instruction::CONST_STRING_JUMBO: {
+ uint32_t string_index = instruction.VRegB_31c();
+ AppendInstruction(
+ new (arena_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc));
+ UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction());
+ break;
+ }
+
+ case Instruction::CONST_CLASS: {
+ uint16_t type_index = instruction.VRegB_21c();
+ bool type_known_final;
+ bool type_known_abstract;
+ bool dont_use_is_referrers_class;
+ // `CanAccessTypeWithoutChecks` will tell whether the method being
+ // built is trying to access its own class, so that the generated
+ // code can optimize for this case. However, the optimization does not
+ // work for inlining, so we use `IsOutermostCompilingClass` instead.
+ bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
+ dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
+ &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
+ AppendInstruction(new (arena_) HLoadClass(
+ graph_->GetCurrentMethod(),
+ type_index,
+ *dex_file_,
+ IsOutermostCompilingClass(type_index),
+ dex_pc,
+ !can_access,
+ compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index)));
+ UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+ break;
+ }
+
+ case Instruction::MOVE_EXCEPTION: {
+ AppendInstruction(new (arena_) HLoadException(dex_pc));
+ UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
+ AppendInstruction(new (arena_) HClearException(dex_pc));
+ break;
+ }
+
+ case Instruction::THROW: {
+ HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot);
+ AppendInstruction(new (arena_) HThrow(exception, dex_pc));
+ // We finished building this block. Set the current block to null to avoid
+ // adding dead instructions to it.
+ current_block_ = nullptr;
+ break;
+ }
+
+ case Instruction::INSTANCE_OF: {
+ uint8_t destination = instruction.VRegA_22c();
+ uint8_t reference = instruction.VRegB_22c();
+ uint16_t type_index = instruction.VRegC_22c();
+ BuildTypeCheck(instruction, destination, reference, type_index, dex_pc);
+ break;
+ }
+
+ case Instruction::CHECK_CAST: {
+ uint8_t reference = instruction.VRegA_21c();
+ uint16_t type_index = instruction.VRegB_21c();
+ BuildTypeCheck(instruction, -1, reference, type_index, dex_pc);
+ break;
+ }
+
+ case Instruction::MONITOR_ENTER: {
+ AppendInstruction(new (arena_) HMonitorOperation(
+ LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
+ HMonitorOperation::OperationKind::kEnter,
+ dex_pc));
+ break;
+ }
+
+ case Instruction::MONITOR_EXIT: {
+ AppendInstruction(new (arena_) HMonitorOperation(
+ LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot),
+ HMonitorOperation::OperationKind::kExit,
+ dex_pc));
+ break;
+ }
+
+ case Instruction::SPARSE_SWITCH:
+ case Instruction::PACKED_SWITCH: {
+ BuildSwitch(instruction, dex_pc);
+ break;
+ }
+
+ default:
+ VLOG(compiler) << "Did not compile "
+ << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
+ << " because of unhandled instruction "
+ << instruction.Name();
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledUnhandledInstruction);
+ return false;
+ }
+ return true;
+} // NOLINT(readability/fn_size)
+
+} // namespace art
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
new file mode 100644
index 0000000000..612594f8a8
--- /dev/null
+++ b/compiler/optimizing/instruction_builder.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
+
+#include "base/arena_containers.h"
+#include "base/arena_object.h"
+#include "block_builder.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_driver-inl.h"
+#include "driver/dex_compilation_unit.h"
+#include "mirror/dex_cache.h"
+#include "nodes.h"
+#include "optimizing_compiler_stats.h"
+#include "ssa_builder.h"
+
+namespace art {
+
+class HInstructionBuilder : public ValueObject {
+ public:
+ HInstructionBuilder(HGraph* graph,
+ HBasicBlockBuilder* block_builder,
+ SsaBuilder* ssa_builder,
+ const DexFile* dex_file,
+ const DexFile::CodeItem& code_item,
+ Primitive::Type return_type,
+ DexCompilationUnit* dex_compilation_unit,
+ const DexCompilationUnit* const outer_compilation_unit,
+ CompilerDriver* driver,
+ const uint8_t* interpreter_metadata,
+ OptimizingCompilerStats* compiler_stats,
+ Handle<mirror::DexCache> dex_cache)
+ : arena_(graph->GetArena()),
+ graph_(graph),
+ dex_file_(dex_file),
+ code_item_(code_item),
+ return_type_(return_type),
+ block_builder_(block_builder),
+ ssa_builder_(ssa_builder),
+ locals_for_(arena_->Adapter(kArenaAllocGraphBuilder)),
+ current_block_(nullptr),
+ current_locals_(nullptr),
+ latest_result_(nullptr),
+ compiler_driver_(driver),
+ dex_compilation_unit_(dex_compilation_unit),
+ outer_compilation_unit_(outer_compilation_unit),
+ interpreter_metadata_(interpreter_metadata),
+ compilation_stats_(compiler_stats),
+ dex_cache_(dex_cache),
+ loop_headers_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)) {
+ loop_headers_.reserve(kDefaultNumberOfLoops);
+ }
+
+ bool Build();
+
+ private:
+ void MaybeRecordStat(MethodCompilationStat compilation_stat);
+
+ void InitializeBlockLocals();
+ void PropagateLocalsToCatchBlocks();
+ void SetLoopHeaderPhiInputs();
+
+ bool ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc);
+ void FindNativeDebugInfoLocations(ArenaBitVector* locations);
+
+ bool CanDecodeQuickenedInfo() const;
+ uint16_t LookupQuickenedInfo(uint32_t dex_pc);
+
+ HBasicBlock* FindBlockStartingAt(uint32_t dex_pc) const;
+
+ ArenaVector<HInstruction*>* GetLocalsFor(HBasicBlock* block);
+ HInstruction* ValueOfLocalAt(HBasicBlock* block, size_t local);
+ HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type) const;
+ void UpdateLocal(uint32_t register_index, HInstruction* instruction);
+
+ void AppendInstruction(HInstruction* instruction);
+ void InsertInstructionAtTop(HInstruction* instruction);
+ void InitializeInstruction(HInstruction* instruction);
+
+ void InitializeParameters();
+
+ // Returns whether the current method needs access check for the type.
+ // Output parameter finalizable is set to whether the type is finalizable.
+ bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const;
+
+ template<typename T>
+ void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_23x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ void Binop_23x_cmp(const Instruction& instruction,
+ Primitive::Type type,
+ ComparisonBias bias,
+ uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_12x_shift(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);
+
+ template<typename T>
+ void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);
+
+ template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
+ template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);
+
+ void Conversion_12x(const Instruction& instruction,
+ Primitive::Type input_type,
+ Primitive::Type result_type,
+ uint32_t dex_pc);
+
+ void BuildCheckedDivRem(uint16_t out_reg,
+ uint16_t first_reg,
+ int64_t second_reg_or_constant,
+ uint32_t dex_pc,
+ Primitive::Type type,
+ bool second_is_lit,
+ bool is_div);
+
+ void BuildReturn(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
+
+ // Builds an instance field access node and returns whether the instruction is supported.
+ bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
+
+ void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_put,
+ Primitive::Type field_type);
+ // Builds a static field access node and returns whether the instruction is supported.
+ bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);
+
+ void BuildArrayAccess(const Instruction& instruction,
+ uint32_t dex_pc,
+ bool is_get,
+ Primitive::Type anticipated_type);
+
+ // Builds an invocation node and returns whether the instruction is supported.
+ bool BuildInvoke(const Instruction& instruction,
+ uint32_t dex_pc,
+ uint32_t method_idx,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index);
+
+ // Builds a new array node and the instructions that fill it.
+ void BuildFilledNewArray(uint32_t dex_pc,
+ uint32_t type_index,
+ uint32_t number_of_vreg_arguments,
+ bool is_range,
+ uint32_t* args,
+ uint32_t register_index);
+
+ void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc);
+
+ // Fills the given object with data as specified in the fill-array-data
+ // instruction. Currently only used for non-reference and non-floating point
+ // arrays.
+ template <typename T>
+ void BuildFillArrayData(HInstruction* object,
+ const T* data,
+ uint32_t element_count,
+ Primitive::Type anticipated_type,
+ uint32_t dex_pc);
+
+ // Fills the given object with data as specified in the fill-array-data
+ // instruction. The data must be for long and double arrays.
+ void BuildFillWideArrayData(HInstruction* object,
+ const int64_t* data,
+ uint32_t element_count,
+ uint32_t dex_pc);
+
+ // Builds a `HInstanceOf`, or a `HCheckCast` instruction.
+ void BuildTypeCheck(const Instruction& instruction,
+ uint8_t destination,
+ uint8_t reference,
+ uint16_t type_index,
+ uint32_t dex_pc);
+
+ // Builds an instruction sequence for a switch statement.
+ void BuildSwitch(const Instruction& instruction, uint32_t dex_pc);
+
+ // Returns the outer-most compiling method's class.
+ mirror::Class* GetOutermostCompilingClass() const;
+
+ // Returns the class whose method is being compiled.
+ mirror::Class* GetCompilingClass() const;
+
+ // Returns whether `type_index` points to the outer-most compiling method's class.
+ bool IsOutermostCompilingClass(uint16_t type_index) const;
+
+ void PotentiallySimplifyFakeString(uint16_t original_dex_register,
+ uint32_t dex_pc,
+ HInvoke* invoke);
+
+ bool SetupInvokeArguments(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ size_t start_index,
+ size_t* argument_index);
+
+ bool HandleInvoke(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor,
+ HClinitCheck* clinit_check);
+
+ bool HandleStringInit(HInvoke* invoke,
+ uint32_t number_of_vreg_arguments,
+ uint32_t* args,
+ uint32_t register_index,
+ bool is_range,
+ const char* descriptor);
+ void HandleStringInitResult(HInvokeStaticOrDirect* invoke);
+
+ HClinitCheck* ProcessClinitCheckForInvoke(
+ uint32_t dex_pc,
+ ArtMethod* method,
+ uint32_t method_idx,
+ HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Build a HNewInstance instruction.
+ bool BuildNewInstance(uint16_t type_index, uint32_t dex_pc);
+
+ // Return whether the compiler can assume `cls` is initialized.
+ bool IsInitialized(Handle<mirror::Class> cls) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Try to resolve a method using the class linker. Return null if a method could
+ // not be resolved.
+ ArtMethod* ResolveMethod(uint16_t method_idx, InvokeType invoke_type);
+
+ ArenaAllocator* const arena_;
+ HGraph* const graph_;
+
+ // The dex file where the method being compiled is, and the bytecode data.
+ const DexFile* const dex_file_;
+ const DexFile::CodeItem& code_item_;
+
+ // The return type of the method being compiled.
+ const Primitive::Type return_type_;
+
+ HBasicBlockBuilder* block_builder_;
+ SsaBuilder* ssa_builder_;
+
+ ArenaVector<ArenaVector<HInstruction*>> locals_for_;
+ HBasicBlock* current_block_;
+ ArenaVector<HInstruction*>* current_locals_;
+ HInstruction* latest_result_;
+
+ CompilerDriver* const compiler_driver_;
+
+ // The compilation unit of the current method being compiled. Note that
+ // it can be an inlined method.
+ DexCompilationUnit* const dex_compilation_unit_;
+
+ // The compilation unit of the outermost method being compiled. That is the
+ // method being compiled (and not inlined), and potentially inlining other
+ // methods.
+ const DexCompilationUnit* const outer_compilation_unit_;
+
+ const uint8_t* interpreter_metadata_;
+ OptimizingCompilerStats* compilation_stats_;
+ Handle<mirror::DexCache> dex_cache_;
+
+ ArenaVector<HBasicBlock*> loop_headers_;
+
+ static constexpr int kDefaultNumberOfLoops = 2;
+
+ DISALLOW_COPY_AND_ASSIGN(HInstructionBuilder);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 5de2306506..a589ef07e2 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -368,17 +368,16 @@ void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) {
GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
}
-static void GenBitCount(HInvoke* instr, bool is_long, vixl::MacroAssembler* masm) {
- DCHECK(instr->GetType() == Primitive::kPrimInt);
- DCHECK((is_long && instr->InputAt(0)->GetType() == Primitive::kPrimLong) ||
- (!is_long && instr->InputAt(0)->GetType() == Primitive::kPrimInt));
+static void GenBitCount(HInvoke* instr, Primitive::Type type, vixl::MacroAssembler* masm) {
+ DCHECK(Primitive::IsIntOrLongType(type)) << type;
+ DCHECK_EQ(instr->GetType(), Primitive::kPrimInt);
+ DCHECK_EQ(Primitive::PrimitiveKind(instr->InputAt(0)->GetType()), type);
- Location out = instr->GetLocations()->Out();
UseScratchRegisterScope temps(masm);
Register src = InputRegisterAt(instr, 0);
- Register dst = is_long ? XRegisterFrom(out) : WRegisterFrom(out);
- FPRegister fpr = is_long ? temps.AcquireD() : temps.AcquireS();
+ Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
+ FPRegister fpr = (type == Primitive::kPrimLong) ? temps.AcquireD() : temps.AcquireS();
__ Fmov(fpr, src);
__ Cnt(fpr.V8B(), fpr.V8B());
@@ -391,7 +390,7 @@ void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) {
- GenBitCount(invoke, /* is_long */ true, GetVIXLAssembler());
+ GenBitCount(invoke, Primitive::kPrimLong, GetVIXLAssembler());
}
void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
@@ -399,7 +398,7 @@ void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) {
- GenBitCount(invoke, /* is_long */ false, GetVIXLAssembler());
+ GenBitCount(invoke, Primitive::kPrimInt, GetVIXLAssembler());
}
static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 1280587276..19c6a225ac 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -614,8 +614,6 @@ static void GenBitCount(LocationSummary* locations,
Primitive::Type type,
bool isR6,
MipsAssembler* assembler) {
- DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
-
Register out = locations->Out().AsRegister<Register>();
// https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
@@ -663,7 +661,8 @@ static void GenBitCount(LocationSummary* locations,
__ MulR2(out, out, TMP);
}
__ Srl(out, out, 24);
- } else if (type == Primitive::kPrimLong) {
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimLong);
Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
Register tmp_hi = locations->GetTemp(0).AsRegister<Register>();
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 95fdb9b3f6..4aab3e2768 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2387,10 +2387,10 @@ static void GenBitCount(X86Assembler* assembler,
if (invoke->InputAt(0)->IsConstant()) {
// Evaluate this at compile time.
int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
- value = is_long
+ int32_t result = is_long
? POPCOUNT(static_cast<uint64_t>(value))
: POPCOUNT(static_cast<uint32_t>(value));
- codegen->Load32BitValue(out, value);
+ codegen->Load32BitValue(out, result);
return;
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 9e568f7b4f..9ca4ef049a 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2402,10 +2402,10 @@ static void GenBitCount(X86_64Assembler* assembler,
if (invoke->InputAt(0)->IsConstant()) {
// Evaluate this at compile time.
int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
- value = is_long
+ int32_t result = is_long
? POPCOUNT(static_cast<uint64_t>(value))
: POPCOUNT(static_cast<uint32_t>(value));
- codegen->Load32BitValue(out, value);
+ codegen->Load32BitValue(out, result);
return;
}
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 3202493c3a..bdaef1d0e9 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -211,8 +211,8 @@ TEST_F(LiveRangesTest, Loop1) {
*
* Which becomes the following graph (numbered by lifetime position):
* 2: constant0
- * 4: constant4
- * 6: constant5
+ * 4: constant5
+ * 6: constant4
* 8: goto
* |
* 12: goto
@@ -247,7 +247,7 @@ TEST_F(LiveRangesTest, Loop1) {
liveness.Analyze();
// Test for the 0 constant.
- LiveInterval* interval = liveness.GetInstructionFromSsaIndex(0)->GetLiveInterval();
+ LiveInterval* interval = graph->GetIntConstant(0)->GetLiveInterval();
LiveRange* range = interval->GetFirstRange();
ASSERT_EQ(2u, range->GetStart());
// Last use is the loop phi so instruction is live until
@@ -256,18 +256,18 @@ TEST_F(LiveRangesTest, Loop1) {
ASSERT_TRUE(range->GetNext() == nullptr);
// Test for the 4 constant.
- interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval();
+ interval = graph->GetIntConstant(4)->GetLiveInterval();
range = interval->GetFirstRange();
// The instruction is live until the end of the loop.
- ASSERT_EQ(4u, range->GetStart());
+ ASSERT_EQ(6u, range->GetStart());
ASSERT_EQ(24u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
// Test for the 5 constant.
- interval = liveness.GetInstructionFromSsaIndex(2)->GetLiveInterval();
+ interval = graph->GetIntConstant(5)->GetLiveInterval();
range = interval->GetFirstRange();
// The instruction is live until the return instruction after the loop.
- ASSERT_EQ(6u, range->GetStart());
+ ASSERT_EQ(4u, range->GetStart());
ASSERT_EQ(26u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 92a987cb1d..bd74368e17 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -154,7 +154,7 @@ TEST_F(LivenessTest, CFG4) {
// return a;
//
// Bitsets are made of:
- // (constant0, constant4, constant5, phi)
+ // (constant0, constant5, constant4, phi)
const char* expected =
"Block 0\n" // entry block
" live in: (0000)\n"
@@ -165,11 +165,11 @@ TEST_F(LivenessTest, CFG4) {
" live out: (0110)\n"
" kill: (0000)\n"
"Block 2\n" // else block
- " live in: (0100)\n"
+ " live in: (0010)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 3\n" // then block
- " live in: (0010)\n"
+ " live in: (0100)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 4\n" // return block
@@ -291,7 +291,7 @@ TEST_F(LivenessTest, Loop3) {
// }
// return 5;
// Bitsets are made of:
- // (constant0, constant4, constant5, phi)
+ // (constant0, constant5, constant4, phi)
const char* expected =
"Block 0\n"
" live in: (0000)\n"
@@ -310,7 +310,7 @@ TEST_F(LivenessTest, Loop3) {
" live out: (0110)\n"
" kill: (0000)\n"
"Block 4\n" // return block
- " live in: (0010)\n"
+ " live in: (0100)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 5\n" // exit block
@@ -386,7 +386,7 @@ TEST_F(LivenessTest, Loop5) {
// Make sure we create a preheader of a loop when a header originally has two
// incoming blocks and one back edge.
// Bitsets are made of:
- // (constant0, constant4, constant5, phi in block 8)
+ // (constant0, constant5, constant4, phi in block 8)
const char* expected =
"Block 0\n"
" live in: (0000)\n"
@@ -397,11 +397,11 @@ TEST_F(LivenessTest, Loop5) {
" live out: (0110)\n"
" kill: (0000)\n"
"Block 2\n"
- " live in: (0100)\n"
+ " live in: (0010)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 3\n"
- " live in: (0010)\n"
+ " live in: (0100)\n"
" live out: (0000)\n"
" kill: (0000)\n"
"Block 4\n" // loop header
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 950448136d..1086cbf503 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -134,46 +134,44 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) {
if (block->IsExitBlock()) {
SetExitBlock(nullptr);
}
+ // Mark the block as removed. This is used by the HGraphBuilder to discard
+ // the block as a branch target.
+ block->SetGraph(nullptr);
}
}
}
GraphAnalysisResult HGraph::BuildDominatorTree() {
- // (1) Simplify the CFG so that catch blocks have only exceptional incoming
- // edges. This invariant simplifies building SSA form because Phis cannot
- // collect both normal- and exceptional-flow values at the same time.
- SimplifyCatchBlocks();
-
ArenaBitVector visited(arena_, blocks_.size(), false, kArenaAllocGraphBuilder);
- // (2) Find the back edges in the graph doing a DFS traversal.
+ // (1) Find the back edges in the graph doing a DFS traversal.
FindBackEdges(&visited);
- // (3) Remove instructions and phis from blocks not visited during
+ // (2) Remove instructions and phis from blocks not visited during
// the initial DFS as users from other instructions, so that
// users can be safely removed before uses later.
RemoveInstructionsAsUsersFromDeadBlocks(visited);
- // (4) Remove blocks not visited during the initial DFS.
+ // (3) Remove blocks not visited during the initial DFS.
// Step (5) requires dead blocks to be removed from the
// predecessors list of live blocks.
RemoveDeadBlocks(visited);
- // (5) Simplify the CFG now, so that we don't need to recompute
+ // (4) Simplify the CFG now, so that we don't need to recompute
// dominators and the reverse post order.
SimplifyCFG();
- // (6) Compute the dominance information and the reverse post order.
+ // (5) Compute the dominance information and the reverse post order.
ComputeDominanceInformation();
- // (7) Analyze loops discovered through back edge analysis, and
+ // (6) Analyze loops discovered through back edge analysis, and
// set the loop information on each block.
GraphAnalysisResult result = AnalyzeLoops();
if (result != kAnalysisSuccess) {
return result;
}
- // (8) Precompute per-block try membership before entering the SSA builder,
+ // (7) Precompute per-block try membership before entering the SSA builder,
// which needs the information to build catch block phis from values of
// locals at throwing instructions inside try blocks.
ComputeTryBlockInformation();
@@ -320,85 +318,10 @@ void HGraph::SimplifyLoop(HBasicBlock* header) {
}
}
- // Place the suspend check at the beginning of the header, so that live registers
- // will be known when allocating registers. Note that code generation can still
- // generate the suspend check at the back edge, but needs to be careful with
- // loop phi spill slots (which are not written to at back edge).
HInstruction* first_instruction = header->GetFirstInstruction();
- if (!first_instruction->IsSuspendCheck()) {
- HSuspendCheck* check = new (arena_) HSuspendCheck(header->GetDexPc());
- header->InsertInstructionBefore(check, first_instruction);
- first_instruction = check;
- }
- info->SetSuspendCheck(first_instruction->AsSuspendCheck());
-}
-
-static bool CheckIfPredecessorAtIsExceptional(const HBasicBlock& block, size_t pred_idx) {
- HBasicBlock* predecessor = block.GetPredecessors()[pred_idx];
- if (!predecessor->EndsWithTryBoundary()) {
- // Only edges from HTryBoundary can be exceptional.
- return false;
- }
- HTryBoundary* try_boundary = predecessor->GetLastInstruction()->AsTryBoundary();
- if (try_boundary->GetNormalFlowSuccessor() == &block) {
- // This block is the normal-flow successor of `try_boundary`, but it could
- // also be one of its exception handlers if catch blocks have not been
- // simplified yet. Predecessors are unordered, so we will consider the first
- // occurrence to be the normal edge and a possible second occurrence to be
- // the exceptional edge.
- return !block.IsFirstIndexOfPredecessor(predecessor, pred_idx);
- } else {
- // This is not the normal-flow successor of `try_boundary`, hence it must be
- // one of its exception handlers.
- DCHECK(try_boundary->HasExceptionHandler(block));
- return true;
- }
-}
-
-void HGraph::SimplifyCatchBlocks() {
- // NOTE: We're 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.
- for (size_t block_id = 0u, end = blocks_.size(); block_id != end; ++block_id) {
- HBasicBlock* catch_block = blocks_[block_id];
- if (catch_block == nullptr || !catch_block->IsCatchBlock()) {
- continue;
- }
-
- bool exceptional_predecessors_only = true;
- for (size_t j = 0; j < catch_block->GetPredecessors().size(); ++j) {
- if (!CheckIfPredecessorAtIsExceptional(*catch_block, j)) {
- exceptional_predecessors_only = false;
- break;
- }
- }
-
- if (!exceptional_predecessors_only) {
- // Catch block has normal-flow predecessors and needs to be simplified.
- // Splitting the block before its first instruction moves all its
- // instructions into `normal_block` and links the two blocks with a Goto.
- // Afterwards, incoming normal-flow edges are re-linked to `normal_block`,
- // leaving `catch_block` with the exceptional edges only.
- //
- // Note that catch blocks with normal-flow predecessors cannot begin with
- // a move-exception instruction, as guaranteed by the verifier. However,
- // trivially dead predecessors are ignored by the verifier and such code
- // has not been removed at this stage. We therefore ignore the assumption
- // and rely on GraphChecker to enforce it after initial DCE is run (b/25492628).
- HBasicBlock* normal_block = catch_block->SplitCatchBlockAfterMoveException();
- if (normal_block == nullptr) {
- // Catch block is either empty or only contains a move-exception. It must
- // therefore be dead and will be removed during initial DCE. Do nothing.
- DCHECK(!catch_block->EndsWithControlFlowInstruction());
- } else {
- // Catch block was split. Re-link normal-flow edges to the new block.
- for (size_t j = 0; j < catch_block->GetPredecessors().size(); ++j) {
- if (!CheckIfPredecessorAtIsExceptional(*catch_block, j)) {
- catch_block->GetPredecessors()[j]->ReplaceSuccessor(catch_block, normal_block);
- --j;
- }
- }
- }
- }
+ if (first_instruction != nullptr && first_instruction->IsSuspendCheck()) {
+ // Called from DeadBlockElimination. Update SuspendCheck pointer.
+ info->SetSuspendCheck(first_instruction->AsSuspendCheck());
}
}
@@ -447,10 +370,9 @@ void HGraph::SimplifyCFG() {
HBasicBlock* successor = normal_successors[j];
DCHECK(!successor->IsCatchBlock());
if (successor == exit_block_) {
- // Throw->TryBoundary->Exit. Special case which we do not want to split
- // because Goto->Exit is not allowed.
+ // (Throw/Return/ReturnVoid)->TryBoundary->Exit. Special case which we
+ // do not want to split because Goto->Exit is not allowed.
DCHECK(block->IsSingleTryBoundary());
- DCHECK(block->GetSinglePredecessor()->GetLastInstruction()->IsThrow());
} else if (successor->GetPredecessors().size() > 1) {
SplitCriticalEdge(block, successor);
// SplitCriticalEdge could have invalidated the `normal_successors`
@@ -463,8 +385,10 @@ void HGraph::SimplifyCFG() {
}
if (block->IsLoopHeader()) {
SimplifyLoop(block);
- } else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) {
- // We are being called by the dead code elimination pass, and what used to be
+ } else if (!block->IsEntryBlock() &&
+ block->GetFirstInstruction() != nullptr &&
+ block->GetFirstInstruction()->IsSuspendCheck()) {
+ // We are being called by the dead code elimiation pass, and what used to be
// a loop got dismantled. Just remove the suspend check.
block->RemoveInstruction(block->GetFirstInstruction());
}
@@ -502,12 +426,25 @@ void HLoopInformation::Dump(std::ostream& os) {
}
void HGraph::InsertConstant(HConstant* constant) {
- // New constants are inserted before the final control-flow instruction
- // of the graph, or at its end if called from the graph builder.
- if (entry_block_->EndsWithControlFlowInstruction()) {
- entry_block_->InsertInstructionBefore(constant, entry_block_->GetLastInstruction());
- } else {
+ // New constants are inserted before the SuspendCheck at the bottom of the
+ // entry block. Note that this method can be called from the graph builder and
+ // the entry block therefore may not end with SuspendCheck->Goto yet.
+ HInstruction* insert_before = nullptr;
+
+ HInstruction* gota = entry_block_->GetLastInstruction();
+ if (gota != nullptr && gota->IsGoto()) {
+ HInstruction* suspend_check = gota->GetPrevious();
+ if (suspend_check != nullptr && suspend_check->IsSuspendCheck()) {
+ insert_before = suspend_check;
+ } else {
+ insert_before = gota;
+ }
+ }
+
+ if (insert_before == nullptr) {
entry_block_->AddInstruction(constant);
+ } else {
+ entry_block_->InsertInstructionBefore(constant, insert_before);
}
}
@@ -1404,34 +1341,6 @@ HBasicBlock* HBasicBlock::CreateImmediateDominator() {
return new_block;
}
-HBasicBlock* HBasicBlock::SplitCatchBlockAfterMoveException() {
- DCHECK(!graph_->IsInSsaForm()) << "Support for SSA form not implemented.";
- DCHECK(IsCatchBlock()) << "This method is intended for catch blocks only.";
-
- HInstruction* first_insn = GetFirstInstruction();
- HInstruction* split_before = nullptr;
-
- if (first_insn != nullptr && first_insn->IsLoadException()) {
- // Catch block starts with a LoadException. Split the block after
- // the StoreLocal and ClearException which must come after the load.
- DCHECK(first_insn->GetNext()->IsStoreLocal());
- DCHECK(first_insn->GetNext()->GetNext()->IsClearException());
- split_before = first_insn->GetNext()->GetNext()->GetNext();
- } else {
- // Catch block does not load the exception. Split at the beginning
- // to create an empty catch block.
- split_before = first_insn;
- }
-
- if (split_before == nullptr) {
- // Catch block has no instructions after the split point (must be dead).
- // Do not split it but rather signal error by returning nullptr.
- return nullptr;
- } else {
- return SplitBefore(split_before);
- }
-}
-
HBasicBlock* HBasicBlock::SplitBeforeForInlining(HInstruction* cursor) {
DCHECK_EQ(cursor->GetBlock(), this);
@@ -1910,6 +1819,7 @@ void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) {
RemoveElement(reverse_post_order_, block);
blocks_[block->GetBlockId()] = nullptr;
+ block->SetGraph(nullptr);
}
void HGraph::UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
@@ -1962,6 +1872,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
instr_it.Advance()) {
HInstruction* current = instr_it.Current();
if (current->NeedsEnvironment()) {
+ DCHECK(current->HasEnvironment());
current->GetEnvironment()->SetAndCopyParentChain(
outer_graph->GetArena(), invoke->GetEnvironment());
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 9425ef3267..0088fed62a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,7 +26,6 @@
#include "base/arena_object.h"
#include "base/stl_util.h"
#include "dex/compiler_enums.h"
-#include "dex_instruction-inl.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
@@ -101,6 +100,7 @@ enum IfCondition {
};
enum GraphAnalysisResult {
+ kAnalysisSkipped,
kAnalysisInvalidBytecode,
kAnalysisFailThrowCatchLoop,
kAnalysisFailAmbiguousArrayOp,
@@ -427,6 +427,10 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
number_of_in_vregs_ = value;
}
+ uint16_t GetNumberOfInVRegs() const {
+ return number_of_in_vregs_;
+ }
+
uint16_t GetNumberOfLocalVRegs() const {
DCHECK(!in_ssa_form_);
return number_of_vregs_ - number_of_in_vregs_;
@@ -999,15 +1003,6 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> {
// Similar to `SplitBeforeForInlining` but does it after `cursor`.
HBasicBlock* SplitAfterForInlining(HInstruction* cursor);
- // Split catch block into two blocks after the original move-exception bytecode
- // instruction, or at the beginning if not present. Returns the newly created,
- // latter block, or nullptr if such block could not be created (must be dead
- // in that case). Note that this method just updates raw block information,
- // like predecessors, successors, dominators, and instruction list. It does not
- // update the graph, reverse post order, loop information, nor make sure the
- // blocks are consistent (for example ending with a control flow instruction).
- HBasicBlock* SplitCatchBlockAfterMoveException();
-
// Merge `other` at the end of `this`. Successors and dominated blocks of
// `other` are changed to be successors and dominated blocks of `this`. Note
// that this method does not update the graph, reverse post order, loop
@@ -1220,9 +1215,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(LessThanOrEqual, Condition) \
M(LoadClass, Instruction) \
M(LoadException, Instruction) \
- M(LoadLocal, Instruction) \
M(LoadString, Instruction) \
- M(Local, Instruction) \
M(LongConstant, Constant) \
M(MemoryBarrier, Instruction) \
M(MonitorOperation, Instruction) \
@@ -1253,7 +1246,6 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(UnresolvedStaticFieldGet, Instruction) \
M(UnresolvedStaticFieldSet, Instruction) \
M(Select, Instruction) \
- M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
M(Throw, Instruction) \
@@ -2392,6 +2384,107 @@ class HReturn : public HTemplateInstruction<1> {
DISALLOW_COPY_AND_ASSIGN(HReturn);
};
+class HPhi : public HInstruction {
+ public:
+ HPhi(ArenaAllocator* arena,
+ uint32_t reg_number,
+ size_t number_of_inputs,
+ Primitive::Type type,
+ uint32_t dex_pc = kNoDexPc)
+ : HInstruction(SideEffects::None(), dex_pc),
+ inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+ reg_number_(reg_number) {
+ SetPackedField<TypeField>(ToPhiType(type));
+ DCHECK_NE(GetType(), Primitive::kPrimVoid);
+ // Phis are constructed live and marked dead if conflicting or unused.
+ // Individual steps of SsaBuilder should assume that if a phi has been
+ // marked dead, it can be ignored and will be removed by SsaPhiElimination.
+ SetPackedFlag<kFlagIsLive>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
+ }
+
+ // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
+ static Primitive::Type ToPhiType(Primitive::Type type) {
+ return Primitive::PrimitiveKind(type);
+ }
+
+ bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
+
+ size_t InputCount() const OVERRIDE { return inputs_.size(); }
+
+ void AddInput(HInstruction* input);
+ void RemoveInputAt(size_t index);
+
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
+ void SetType(Primitive::Type new_type) {
+ // Make sure that only valid type changes occur. The following are allowed:
+ // (1) int -> float/ref (primitive type propagation),
+ // (2) long -> double (primitive type propagation).
+ DCHECK(GetType() == new_type ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
+ (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
+ SetPackedField<TypeField>(new_type);
+ }
+
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
+
+ uint32_t GetRegNumber() const { return reg_number_; }
+
+ void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
+ void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
+ bool IsDead() const { return !IsLive(); }
+ bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
+
+ bool IsVRegEquivalentOf(HInstruction* other) const {
+ return other != nullptr
+ && other->IsPhi()
+ && other->AsPhi()->GetBlock() == GetBlock()
+ && other->AsPhi()->GetRegNumber() == GetRegNumber();
+ }
+
+ // Returns the next equivalent phi (starting from the current one) or null if there is none.
+ // An equivalent phi is a phi having the same dex register and type.
+ // It assumes that phis with the same dex register are adjacent.
+ HPhi* GetNextEquivalentPhiWithSameType() {
+ HInstruction* next = GetNext();
+ while (next != nullptr && next->AsPhi()->GetRegNumber() == reg_number_) {
+ if (next->GetType() == GetType()) {
+ return next->AsPhi();
+ }
+ next = next->GetNext();
+ }
+ return nullptr;
+ }
+
+ DECLARE_INSTRUCTION(Phi);
+
+ protected:
+ const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
+ return inputs_[index];
+ }
+
+ void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
+ inputs_[index] = input;
+ }
+
+ private:
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
+ static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
+ static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
+
+ ArenaVector<HUserRecord<HInstruction*> > inputs_;
+ const uint32_t reg_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(HPhi);
+};
+
// The exit instruction is the only instruction of the exit block.
// Instructions aborting the method (HThrow and HReturn) must branch to the
// exit block.
@@ -3552,57 +3645,6 @@ class HCompare : public HBinaryOperation {
DISALLOW_COPY_AND_ASSIGN(HCompare);
};
-// A local in the graph. Corresponds to a Dex register.
-class HLocal : public HTemplateInstruction<0> {
- public:
- explicit HLocal(uint16_t reg_number)
- : HTemplateInstruction(SideEffects::None(), kNoDexPc), reg_number_(reg_number) {}
-
- DECLARE_INSTRUCTION(Local);
-
- uint16_t GetRegNumber() const { return reg_number_; }
-
- private:
- // The Dex register number.
- const uint16_t reg_number_;
-
- DISALLOW_COPY_AND_ASSIGN(HLocal);
-};
-
-// Load a given local. The local is an input of this instruction.
-class HLoadLocal : public HExpression<1> {
- public:
- HLoadLocal(HLocal* local, Primitive::Type type, uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc) {
- SetRawInputAt(0, local);
- }
-
- HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); }
-
- DECLARE_INSTRUCTION(LoadLocal);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HLoadLocal);
-};
-
-// Store a value in a given local. This instruction has two inputs: the value
-// and the local.
-class HStoreLocal : public HTemplateInstruction<2> {
- public:
- HStoreLocal(HLocal* local, HInstruction* value, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc) {
- SetRawInputAt(0, local);
- SetRawInputAt(1, value);
- }
-
- HLocal* GetLocal() const { return reinterpret_cast<HLocal*>(InputAt(0)); }
-
- DECLARE_INSTRUCTION(StoreLocal);
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HStoreLocal);
-};
-
class HNewInstance : public HExpression<2> {
public:
HNewInstance(HInstruction* cls,
@@ -3923,8 +3965,7 @@ class HInvokeStaticOrDirect : public HInvoke {
// potentially one other if the clinit check is explicit, and potentially
// one other if the method is a string factory.
(NeedsCurrentMethodInput(dispatch_info.method_load_kind) ? 1u : 0u) +
- (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u) +
- (dispatch_info.method_load_kind == MethodLoadKind::kStringInit ? 1u : 0u),
+ (clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u),
return_type,
dex_pc,
method_index,
@@ -4052,15 +4093,6 @@ class HInvokeStaticOrDirect : public HInvoke {
DCHECK(!IsStaticWithExplicitClinitCheck());
}
- HInstruction* GetAndRemoveThisArgumentOfStringInit() {
- DCHECK(IsStringInit());
- size_t index = InputCount() - 1;
- HInstruction* input = InputAt(index);
- RemoveAsUserOfInput(index);
- inputs_.pop_back();
- return input;
- }
-
// Is this a call to a static method whose declaring class has an
// explicit initialization check in the graph?
bool IsStaticWithExplicitClinitCheck() const {
@@ -4903,7 +4935,6 @@ class HTypeConversion : public HExpression<1> {
SideEffectsForArchRuntimeCalls(input->GetType(), result_type),
dex_pc) {
SetRawInputAt(0, input);
- DCHECK_NE(input->GetType(), result_type);
// Invariant: We should never generate a conversion to a Boolean value.
DCHECK_NE(Primitive::kPrimBoolean, result_type);
}
@@ -4939,115 +4970,6 @@ class HTypeConversion : public HExpression<1> {
static constexpr uint32_t kNoRegNumber = -1;
-class HPhi : public HInstruction {
- public:
- HPhi(ArenaAllocator* arena,
- uint32_t reg_number,
- size_t number_of_inputs,
- Primitive::Type type,
- uint32_t dex_pc = kNoDexPc)
- : HInstruction(SideEffects::None(), dex_pc),
- inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
- reg_number_(reg_number) {
- SetPackedField<TypeField>(ToPhiType(type));
- DCHECK_NE(GetType(), Primitive::kPrimVoid);
- // Phis are constructed live and marked dead if conflicting or unused.
- // Individual steps of SsaBuilder should assume that if a phi has been
- // marked dead, it can be ignored and will be removed by SsaPhiElimination.
- SetPackedFlag<kFlagIsLive>(true);
- SetPackedFlag<kFlagCanBeNull>(true);
- }
-
- // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
- static Primitive::Type ToPhiType(Primitive::Type type) {
- switch (type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimShort:
- case Primitive::kPrimChar:
- return Primitive::kPrimInt;
- default:
- return type;
- }
- }
-
- bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
-
- size_t InputCount() const OVERRIDE { return inputs_.size(); }
-
- void AddInput(HInstruction* input);
- void RemoveInputAt(size_t index);
-
- Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
- void SetType(Primitive::Type new_type) {
- // Make sure that only valid type changes occur. The following are allowed:
- // (1) int -> float/ref (primitive type propagation),
- // (2) long -> double (primitive type propagation).
- DCHECK(GetType() == new_type ||
- (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
- (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
- (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
- SetPackedField<TypeField>(new_type);
- }
-
- bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
- void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
-
- uint32_t GetRegNumber() const { return reg_number_; }
-
- void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
- void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
- bool IsDead() const { return !IsLive(); }
- bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
-
- bool IsVRegEquivalentOf(HInstruction* other) const {
- return other != nullptr
- && other->IsPhi()
- && other->AsPhi()->GetBlock() == GetBlock()
- && other->AsPhi()->GetRegNumber() == GetRegNumber();
- }
-
- // Returns the next equivalent phi (starting from the current one) or null if there is none.
- // An equivalent phi is a phi having the same dex register and type.
- // It assumes that phis with the same dex register are adjacent.
- HPhi* GetNextEquivalentPhiWithSameType() {
- HInstruction* next = GetNext();
- while (next != nullptr && next->AsPhi()->GetRegNumber() == reg_number_) {
- if (next->GetType() == GetType()) {
- return next->AsPhi();
- }
- next = next->GetNext();
- }
- return nullptr;
- }
-
- DECLARE_INSTRUCTION(Phi);
-
- protected:
- const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
- return inputs_[index];
- }
-
- void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) OVERRIDE {
- inputs_[index] = input;
- }
-
- private:
- static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
- static constexpr size_t kFieldTypeSize =
- MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
- static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
- static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
- static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
- static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
- using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
-
- ArenaVector<HUserRecord<HInstruction*> > inputs_;
- const uint32_t reg_number_;
-
- DISALLOW_COPY_AND_ASSIGN(HPhi);
-};
-
class HNullCheck : public HExpression<1> {
public:
// `HNullCheck` can trigger GC, as it may call the `NullPointerException`
@@ -5391,7 +5313,7 @@ class HBoundsCheck : public HExpression<2> {
// constructor.
HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
: HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
- DCHECK(index->GetType() == Primitive::kPrimInt);
+ DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
SetRawInputAt(0, index);
SetRawInputAt(1, length);
}
@@ -5415,7 +5337,7 @@ class HBoundsCheck : public HExpression<2> {
class HSuspendCheck : public HTemplateInstruction<0> {
public:
- explicit HSuspendCheck(uint32_t dex_pc)
+ explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
bool NeedsEnvironment() const OVERRIDE {
@@ -5922,7 +5844,7 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
- DCHECK_EQ(field_type, value->GetType());
+ DCHECK_EQ(Primitive::PrimitiveKind(field_type), Primitive::PrimitiveKind(value->GetType()));
SetRawInputAt(0, obj);
SetRawInputAt(1, value);
}
@@ -5982,7 +5904,7 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
field_index_(field_index) {
SetPackedField<FieldTypeField>(field_type);
- DCHECK_EQ(field_type, value->GetType());
+ DCHECK_EQ(Primitive::PrimitiveKind(field_type), Primitive::PrimitiveKind(value->GetType()));
SetRawInputAt(0, value);
}
@@ -6707,74 +6629,6 @@ inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) {
FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
#undef INSTRUCTION_TYPE_CHECK
-class SwitchTable : public ValueObject {
- public:
- SwitchTable(const Instruction& instruction, uint32_t dex_pc, bool sparse)
- : instruction_(instruction), dex_pc_(dex_pc), sparse_(sparse) {
- int32_t table_offset = instruction.VRegB_31t();
- const uint16_t* table = reinterpret_cast<const uint16_t*>(&instruction) + table_offset;
- if (sparse) {
- CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
- } else {
- CHECK_EQ(table[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
- }
- num_entries_ = table[1];
- values_ = reinterpret_cast<const int32_t*>(&table[2]);
- }
-
- uint16_t GetNumEntries() const {
- return num_entries_;
- }
-
- void CheckIndex(size_t index) const {
- if (sparse_) {
- // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
- DCHECK_LT(index, 2 * static_cast<size_t>(num_entries_));
- } else {
- // In a packed table, we have the starting key and num_entries_ values.
- DCHECK_LT(index, 1 + static_cast<size_t>(num_entries_));
- }
- }
-
- int32_t GetEntryAt(size_t index) const {
- CheckIndex(index);
- return values_[index];
- }
-
- uint32_t GetDexPcForIndex(size_t index) const {
- CheckIndex(index);
- return dex_pc_ +
- (reinterpret_cast<const int16_t*>(values_ + index) -
- reinterpret_cast<const int16_t*>(&instruction_));
- }
-
- // Index of the first value in the table.
- size_t GetFirstValueIndex() const {
- if (sparse_) {
- // In a sparse table, we have num_entries_ keys and num_entries_ values, in that order.
- return num_entries_;
- } else {
- // In a packed table, we have the starting key and num_entries_ values.
- return 1;
- }
- }
-
- private:
- const Instruction& instruction_;
- const uint32_t dex_pc_;
-
- // Whether this is a sparse-switch table (or a packed-switch one).
- const bool sparse_;
-
- // This can't be const as it needs to be computed off of the given instruction, and complicated
- // expressions in the initializer list seemed very ugly.
- uint16_t num_entries_;
-
- const int32_t* values_;
-
- DISALLOW_COPY_AND_ASSIGN(SwitchTable);
-};
-
// Create space in `blocks` for adding `number_of_new_blocks` entries
// starting at location `at`. Blocks after `at` are moved accordingly.
inline void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 886c9e2ad4..3d6bf62d0b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -727,14 +727,20 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
&dex_compilation_unit,
&dex_compilation_unit,
&dex_file,
+ *code_item,
compiler_driver,
compilation_stats_.get(),
interpreter_metadata,
- dex_cache);
- GraphAnalysisResult result = builder.BuildGraph(*code_item, &handles);
+ dex_cache,
+ &handles);
+ GraphAnalysisResult result = builder.BuildGraph();
if (result != kAnalysisSuccess) {
switch (result) {
+ case kAnalysisSkipped:
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledSkipped);
+ break;
case kAnalysisInvalidBytecode:
+ MaybeRecordStat(MethodCompilationStat::kNotCompiledInvalidBytecode);
break;
case kAnalysisFailThrowCatchLoop:
MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 3717926a97..9cc6ea45d0 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -38,7 +38,8 @@ enum MethodCompilationStat {
kRemovedCheckedCast,
kRemovedDeadInstruction,
kRemovedNullCheck,
- kNotCompiledBranchOutsideMethodCode,
+ kNotCompiledSkipped,
+ kNotCompiledInvalidBytecode,
kNotCompiledThrowCatchLoop,
kNotCompiledAmbiguousArrayOp,
kNotCompiledHugeMethod,
@@ -115,7 +116,8 @@ class OptimizingCompilerStats {
case kRemovedCheckedCast: name = "RemovedCheckedCast"; break;
case kRemovedDeadInstruction: name = "RemovedDeadInstruction"; break;
case kRemovedNullCheck: name = "RemovedNullCheck"; break;
- case kNotCompiledBranchOutsideMethodCode: name = "NotCompiledBranchOutsideMethodCode"; break;
+ case kNotCompiledSkipped: name = "NotCompiledSkipped"; break;
+ case kNotCompiledInvalidBytecode: name = "NotCompiledInvalidBytecode"; break;
case kNotCompiledThrowCatchLoop : name = "NotCompiledThrowCatchLoop"; break;
case kNotCompiledAmbiguousArrayOp : name = "NotCompiledAmbiguousArrayOp"; break;
case kNotCompiledHugeMethod : name = "NotCompiledHugeMethod"; break;
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 0ca7305d13..dd5cb1c9bb 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -91,8 +91,8 @@ inline HGraph* CreateCFG(ArenaAllocator* allocator,
{
ScopedObjectAccess soa(Thread::Current());
StackHandleScopeCollection handles(soa.Self());
- HGraphBuilder builder(graph, return_type);
- bool graph_built = (builder.BuildGraph(*item, &handles) == kAnalysisSuccess);
+ HGraphBuilder builder(graph, *item, &handles, return_type);
+ bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
return graph_built ? graph : nullptr;
}
}
@@ -109,7 +109,8 @@ inline std::string Patch(const std::string& original, const diff_t& diff) {
std::string result = original;
for (const auto& p : diff) {
std::string::size_type pos = result.find(p.first);
- EXPECT_NE(pos, std::string::npos);
+ DCHECK_NE(pos, std::string::npos)
+ << "Could not find: \"" << p.first << "\" in \"" << result << "\"";
result.replace(pos, p.first.size(), p.second);
}
return result;
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index d5b95d284a..951cdfbd8b 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -44,27 +44,27 @@ TEST_F(PrettyPrinterTest, ReturnVoid) {
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 2: SuspendCheck\n"
- " 3: Goto 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 2\n"
- " 0: ReturnVoid\n"
+ " 2: ReturnVoid\n"
"BasicBlock 2, pred: 1\n"
- " 1: Exit\n";
+ " 3: Exit\n";
TestCode(data, expected);
}
TEST_F(PrettyPrinterTest, CFG1) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 3: SuspendCheck\n"
- " 4: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 0: Goto 2\n"
- "BasicBlock 2, pred: 1, succ: 3\n"
- " 1: ReturnVoid\n"
- "BasicBlock 3, pred: 2\n"
- " 2: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 2: Goto 2\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 3: ReturnVoid\n"
+ "BasicBlock 3, pred: 2\n"
+ " 4: Exit\n";
const uint16_t data[] =
ZERO_REGISTER_CODE_ITEM(
@@ -76,17 +76,17 @@ TEST_F(PrettyPrinterTest, CFG1) {
TEST_F(PrettyPrinterTest, CFG2) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 4: SuspendCheck\n"
- " 5: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 0: Goto 2\n"
- "BasicBlock 2, pred: 1, succ: 3\n"
- " 1: Goto 3\n"
- "BasicBlock 3, pred: 2, succ: 4\n"
- " 2: ReturnVoid\n"
- "BasicBlock 4, pred: 3\n"
- " 3: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 2: Goto 2\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 3: Goto 3\n"
+ "BasicBlock 3, pred: 2, succ: 4\n"
+ " 4: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 5: Exit\n";
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x100,
@@ -98,17 +98,17 @@ TEST_F(PrettyPrinterTest, CFG2) {
TEST_F(PrettyPrinterTest, CFG3) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 4: SuspendCheck\n"
- " 5: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 3\n"
- " 0: Goto 3\n"
- "BasicBlock 2, pred: 3, succ: 4\n"
- " 1: ReturnVoid\n"
- "BasicBlock 3, pred: 1, succ: 2\n"
- " 2: Goto 2\n"
- "BasicBlock 4, pred: 2\n"
- " 3: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
+ " 2: Goto 3\n"
+ "BasicBlock 2, pred: 3, succ: 4\n"
+ " 4: ReturnVoid\n"
+ "BasicBlock 3, pred: 1, succ: 2\n"
+ " 3: Goto 2\n"
+ "BasicBlock 4, pred: 2\n"
+ " 5: Exit\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::GOTO | 0x200,
@@ -134,14 +134,14 @@ TEST_F(PrettyPrinterTest, CFG3) {
TEST_F(PrettyPrinterTest, CFG4) {
const char* expected =
- "BasicBlock 0, succ: 3\n"
- " 2: SuspendCheck\n"
- " 3: Goto 3\n"
- "BasicBlock 1, pred: 3, 1, succ: 1\n"
- " 5: SuspendCheck\n"
- " 0: Goto 1\n"
- "BasicBlock 3, pred: 0, succ: 1\n"
- " 4: Goto 1\n";
+ "BasicBlock 0, succ: 3\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 3\n"
+ "BasicBlock 1, pred: 3, 1, succ: 1\n"
+ " 3: SuspendCheck\n"
+ " 4: Goto 1\n"
+ "BasicBlock 3, pred: 0, succ: 1\n"
+ " 0: Goto 1\n";
const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM(
Instruction::NOP,
@@ -157,13 +157,13 @@ TEST_F(PrettyPrinterTest, CFG4) {
TEST_F(PrettyPrinterTest, CFG5) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 3: SuspendCheck\n"
- " 4: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 3\n"
- " 0: ReturnVoid\n"
- "BasicBlock 3, pred: 1\n"
- " 2: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 3\n"
+ " 2: ReturnVoid\n"
+ "BasicBlock 3, pred: 1\n"
+ " 3: Exit\n";
const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
Instruction::RETURN_VOID,
@@ -175,21 +175,21 @@ TEST_F(PrettyPrinterTest, CFG5) {
TEST_F(PrettyPrinterTest, CFG6) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 1: IntConstant [5, 5]\n"
- " 10: SuspendCheck\n"
- " 11: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 5, 2\n"
- " 5: Equal(1, 1) [6]\n"
- " 6: If(5)\n"
- "BasicBlock 2, pred: 1, succ: 3\n"
- " 7: Goto 3\n"
- "BasicBlock 3, pred: 5, 2, succ: 4\n"
- " 8: ReturnVoid\n"
- "BasicBlock 4, pred: 3\n"
- " 9: Exit\n"
- "BasicBlock 5, pred: 1, succ: 3\n"
- " 12: Goto 3\n";
+ "BasicBlock 0, succ: 1\n"
+ " 3: IntConstant [4, 4]\n"
+ " 1: SuspendCheck\n"
+ " 2: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5, 2\n"
+ " 4: Equal(3, 3) [5]\n"
+ " 5: If(4)\n"
+ "BasicBlock 2, pred: 1, succ: 3\n"
+ " 6: Goto 3\n"
+ "BasicBlock 3, pred: 5, 2, succ: 4\n"
+ " 7: ReturnVoid\n"
+ "BasicBlock 4, pred: 3\n"
+ " 8: Exit\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 0: Goto 3\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -202,22 +202,22 @@ TEST_F(PrettyPrinterTest, CFG6) {
TEST_F(PrettyPrinterTest, CFG7) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 1: IntConstant [5, 5]\n"
- " 10: SuspendCheck\n"
- " 11: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 5, 6\n"
- " 5: Equal(1, 1) [6]\n"
- " 6: If(5)\n"
- "BasicBlock 2, pred: 6, 3, succ: 3\n"
- " 7: Goto 3\n"
- "BasicBlock 3, pred: 5, 2, succ: 2\n"
- " 14: SuspendCheck\n"
- " 8: Goto 2\n"
- "BasicBlock 5, pred: 1, succ: 3\n"
- " 12: Goto 3\n"
- "BasicBlock 6, pred: 1, succ: 2\n"
- " 13: Goto 2\n";
+ "BasicBlock 0, succ: 1\n"
+ " 4: IntConstant [5, 5]\n"
+ " 2: SuspendCheck\n"
+ " 3: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 5, 6\n"
+ " 5: Equal(4, 4) [6]\n"
+ " 6: If(5)\n"
+ "BasicBlock 2, pred: 6, 3, succ: 3\n"
+ " 11: Goto 3\n"
+ "BasicBlock 3, pred: 5, 2, succ: 2\n"
+ " 8: SuspendCheck\n"
+ " 9: Goto 2\n"
+ "BasicBlock 5, pred: 1, succ: 3\n"
+ " 0: Goto 3\n"
+ "BasicBlock 6, pred: 1, succ: 2\n"
+ " 1: Goto 2\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
@@ -230,14 +230,14 @@ TEST_F(PrettyPrinterTest, CFG7) {
TEST_F(PrettyPrinterTest, IntConstant) {
const char* expected =
- "BasicBlock 0, succ: 1\n"
- " 1: IntConstant\n"
- " 5: SuspendCheck\n"
- " 6: Goto 1\n"
- "BasicBlock 1, pred: 0, succ: 2\n"
- " 3: ReturnVoid\n"
- "BasicBlock 2, pred: 1\n"
- " 4: Exit\n";
+ "BasicBlock 0, succ: 1\n"
+ " 2: IntConstant\n"
+ " 0: SuspendCheck\n"
+ " 1: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 3: ReturnVoid\n"
+ "BasicBlock 2, pred: 1\n"
+ " 4: Exit\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
Instruction::CONST_4 | 0 | 0,
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 294d00f8e2..eeadbeb0d1 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -16,36 +16,16 @@
#include "ssa_builder.h"
+#include "bytecode_utils.h"
#include "nodes.h"
#include "reference_type_propagation.h"
#include "ssa_phi_elimination.h"
namespace art {
-void SsaBuilder::SetLoopHeaderPhiInputs() {
- for (size_t i = loop_headers_.size(); i > 0; --i) {
- HBasicBlock* block = loop_headers_[i - 1];
- for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
- HPhi* phi = it.Current()->AsPhi();
- size_t vreg = phi->GetRegNumber();
- for (HBasicBlock* predecessor : block->GetPredecessors()) {
- HInstruction* value = ValueOfLocal(predecessor, vreg);
- if (value == nullptr) {
- // Vreg is undefined at this predecessor. Mark it dead and leave with
- // fewer inputs than predecessors. SsaChecker will fail if not removed.
- phi->SetDead();
- break;
- } else {
- phi->AddInput(value);
- }
- }
- }
- }
-}
-
void SsaBuilder::FixNullConstantType() {
// The order doesn't matter here.
- for (HReversePostOrderIterator itb(*GetGraph()); !itb.Done(); itb.Advance()) {
+ for (HReversePostOrderIterator itb(*graph_); !itb.Done(); itb.Advance()) {
for (HInstructionIterator it(itb.Current()->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* equality_instr = it.Current();
if (!equality_instr->IsEqual() && !equality_instr->IsNotEqual()) {
@@ -70,14 +50,14 @@ void SsaBuilder::FixNullConstantType() {
// can only be the 0 constant.
DCHECK(int_operand->IsIntConstant()) << int_operand->DebugName();
DCHECK_EQ(0, int_operand->AsIntConstant()->GetValue());
- equality_instr->ReplaceInput(GetGraph()->GetNullConstant(), int_operand == right ? 1 : 0);
+ equality_instr->ReplaceInput(graph_->GetNullConstant(), int_operand == right ? 1 : 0);
}
}
}
void SsaBuilder::EquivalentPhisCleanup() {
// The order doesn't matter here.
- for (HReversePostOrderIterator itb(*GetGraph()); !itb.Done(); itb.Advance()) {
+ for (HReversePostOrderIterator itb(*graph_); !itb.Done(); itb.Advance()) {
for (HInstructionIterator it(itb.Current()->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
HPhi* next = phi->GetNextEquivalentPhiWithSameType();
@@ -99,7 +79,7 @@ void SsaBuilder::EquivalentPhisCleanup() {
}
void SsaBuilder::FixEnvironmentPhis() {
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) {
HPhi* phi = it_phis.Current()->AsPhi();
@@ -253,9 +233,9 @@ bool SsaBuilder::UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist) {
}
void SsaBuilder::RunPrimitiveTypePropagation() {
- ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter());
+ ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter());
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+ for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsLoopHeader()) {
for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
@@ -299,8 +279,14 @@ void SsaBuilder::ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* wor
static HArrayGet* FindFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
Primitive::Type type = aget->GetType();
DCHECK(Primitive::IsIntOrLongType(type));
- HArrayGet* next = aget->GetNext()->AsArrayGet();
- return (next != nullptr && next->IsEquivalentOf(aget)) ? next : nullptr;
+ HInstruction* next = aget->GetNext();
+ if (next != nullptr && next->IsArrayGet()) {
+ HArrayGet* next_aget = next->AsArrayGet();
+ if (next_aget->IsEquivalentOf(aget)) {
+ return next_aget;
+ }
+ }
+ return nullptr;
}
static HArrayGet* CreateFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget) {
@@ -333,7 +319,7 @@ bool SsaBuilder::FixAmbiguousArrayOps() {
// uses (because they are untyped) and environment uses (if --debuggable).
// After resolving all ambiguous ArrayGets, we will re-run primitive type
// propagation on the Phis which need to be updated.
- ArenaVector<HPhi*> worklist(GetGraph()->GetArena()->Adapter());
+ ArenaVector<HPhi*> worklist(graph_->GetArena()->Adapter());
{
ScopedObjectAccess soa(Thread::Current());
@@ -451,7 +437,7 @@ static bool HasAliasInEnvironments(HInstruction* instruction) {
}
void SsaBuilder::RemoveRedundantUninitializedStrings() {
- if (GetGraph()->IsDebuggable()) {
+ if (graph_->IsDebuggable()) {
// Do not perform the optimization for consistency with the interpreter
// which always allocates an object for new-instance of String.
return;
@@ -459,11 +445,13 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() {
for (HNewInstance* new_instance : uninitialized_strings_) {
DCHECK(new_instance->IsInBlock());
+ DCHECK(new_instance->IsStringAlloc());
+
// Replace NewInstance of String with NullConstant if not used prior to
// calling StringFactory. In case of deoptimization, the interpreter is
// expected to skip null check on the `this` argument of the StringFactory call.
if (!new_instance->HasNonEnvironmentUses() && !HasAliasInEnvironments(new_instance)) {
- new_instance->ReplaceWith(GetGraph()->GetNullConstant());
+ new_instance->ReplaceWith(graph_->GetNullConstant());
new_instance->GetBlock()->RemoveInstruction(new_instance);
// Remove LoadClass if not needed any more.
@@ -494,57 +482,47 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() {
}
GraphAnalysisResult SsaBuilder::BuildSsa() {
- DCHECK(!GetGraph()->IsInSsaForm());
-
- // 1) Visit in reverse post order. We need to have all predecessors of a block
- // visited (with the exception of loops) in order to create the right environment
- // for that block. For loops, we create phis whose inputs will be set in 2).
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
- VisitBasicBlock(it.Current());
- }
+ DCHECK(!graph_->IsInSsaForm());
- // 2) Set inputs of loop header phis.
- SetLoopHeaderPhiInputs();
-
- // 3) Propagate types of phis. At this point, phis are typed void in the general
+ // 1) Propagate types of phis. At this point, phis are typed void in the general
// case, or float/double/reference if we created an equivalent phi. So we need
// to propagate the types across phis to give them a correct type. If a type
// conflict is detected in this stage, the phi is marked dead.
RunPrimitiveTypePropagation();
- // 4) Now that the correct primitive types have been assigned, we can get rid
+ // 2) Now that the correct primitive types have been assigned, we can get rid
// of redundant phis. Note that we cannot do this phase before type propagation,
// otherwise we could get rid of phi equivalents, whose presence is a requirement
// for the type propagation phase. Note that this is to satisfy statement (a)
// of the SsaBuilder (see ssa_builder.h).
- SsaRedundantPhiElimination(GetGraph()).Run();
+ SsaRedundantPhiElimination(graph_).Run();
- // 5) Fix the type for null constants which are part of an equality comparison.
+ // 3) Fix the type for null constants which are part of an equality comparison.
// We need to do this after redundant phi elimination, to ensure the only cases
// that we can see are reference comparison against 0. The redundant phi
// elimination ensures we do not see a phi taking two 0 constants in a HEqual
// or HNotEqual.
FixNullConstantType();
- // 6) Compute type of reference type instructions. The pass assumes that
+ // 4) Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
- ReferenceTypePropagation(GetGraph(), handles_, /* is_first_run */ true).Run();
+ ReferenceTypePropagation(graph_, handles_, /* is_first_run */ true).Run();
- // 7) Step 1) duplicated ArrayGet instructions with ambiguous type (int/float
- // or long/double) and marked ArraySets with ambiguous input type. Now that RTP
- // computed the type of the array input, the ambiguity can be resolved and the
- // correct equivalents kept.
+ // 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
+ // (int/float or long/double) and marked ArraySets with ambiguous input type.
+ // Now that RTP computed the type of the array input, the ambiguity can be
+ // resolved and the correct equivalents kept.
if (!FixAmbiguousArrayOps()) {
return kAnalysisFailAmbiguousArrayOp;
}
- // 8) Mark dead phis. This will mark phis which are not used by instructions
+ // 6) Mark dead phis. This will mark phis which are not used by instructions
// or other live phis. If compiling as debuggable code, phis will also be kept
// live if they have an environment use.
- SsaDeadPhiElimination dead_phi_elimimation(GetGraph());
+ SsaDeadPhiElimination dead_phi_elimimation(graph_);
dead_phi_elimimation.MarkDeadPhis();
- // 9) Make sure environments use the right phi equivalent: a phi marked dead
+ // 7) Make sure environments use the right phi equivalent: a phi marked dead
// can have a phi equivalent that is not dead. In that case we have to replace
// it with the live equivalent because deoptimization and try/catch rely on
// environments containing values of all live vregs at that point. Note that
@@ -553,166 +531,26 @@ GraphAnalysisResult SsaBuilder::BuildSsa() {
// environments to just reference one.
FixEnvironmentPhis();
- // 10) Now that the right phis are used for the environments, we can eliminate
+ // 8) Now that the right phis are used for the environments, we can eliminate
// phis we do not need. Regardless of the debuggable status, this phase is
/// necessary for statement (b) of the SsaBuilder (see ssa_builder.h), as well
// as for the code generation, which does not deal with phis of conflicting
// input types.
dead_phi_elimimation.EliminateDeadPhis();
- // 11) Step 1) replaced uses of NewInstances of String with the results of
- // their corresponding StringFactory calls. Unless the String objects are used
- // before they are initialized, they can be replaced with NullConstant.
- // Note that this optimization is valid only if unsimplified code does not use
- // the uninitialized value because we assume execution can be deoptimized at
- // any safepoint. We must therefore perform it before any other optimizations.
+ // 9) HInstructionBuidler replaced uses of NewInstances of String with the
+ // results of their corresponding StringFactory calls. Unless the String
+ // objects are used before they are initialized, they can be replaced with
+ // NullConstant. Note that this optimization is valid only if unsimplified
+ // code does not use the uninitialized value because we assume execution can
+ // be deoptimized at any safepoint. We must therefore perform it before any
+ // other optimizations.
RemoveRedundantUninitializedStrings();
- // 12) Clear locals.
- for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions());
- !it.Done();
- it.Advance()) {
- HInstruction* current = it.Current();
- if (current->IsLocal()) {
- current->GetBlock()->RemoveInstruction(current);
- }
- }
-
- GetGraph()->SetInSsaForm();
+ graph_->SetInSsaForm();
return kAnalysisSuccess;
}
-ArenaVector<HInstruction*>* SsaBuilder::GetLocalsFor(HBasicBlock* block) {
- ArenaVector<HInstruction*>* locals = &locals_for_[block->GetBlockId()];
- const size_t vregs = GetGraph()->GetNumberOfVRegs();
- if (locals->empty() && vregs != 0u) {
- locals->resize(vregs, nullptr);
-
- if (block->IsCatchBlock()) {
- ArenaAllocator* arena = GetGraph()->GetArena();
- // We record incoming inputs of catch phis at throwing instructions and
- // must therefore eagerly create the phis. Phis for undefined vregs will
- // be deleted when the first throwing instruction with the vreg undefined
- // is encountered. Unused phis will be removed by dead phi analysis.
- for (size_t i = 0; i < vregs; ++i) {
- // No point in creating the catch phi if it is already undefined at
- // the first throwing instruction.
- HInstruction* current_local_value = (*current_locals_)[i];
- if (current_local_value != nullptr) {
- HPhi* phi = new (arena) HPhi(
- arena,
- i,
- 0,
- current_local_value->GetType());
- block->AddPhi(phi);
- (*locals)[i] = phi;
- }
- }
- }
- }
- return locals;
-}
-
-HInstruction* SsaBuilder::ValueOfLocal(HBasicBlock* block, size_t local) {
- ArenaVector<HInstruction*>* locals = GetLocalsFor(block);
- return (*locals)[local];
-}
-
-void SsaBuilder::VisitBasicBlock(HBasicBlock* block) {
- current_locals_ = GetLocalsFor(block);
-
- if (block->IsCatchBlock()) {
- // Catch phis were already created and inputs collected from throwing sites.
- if (kIsDebugBuild) {
- // Make sure there was at least one throwing instruction which initialized
- // locals (guaranteed by HGraphBuilder) and that all try blocks have been
- // visited already (from HTryBoundary scoping and reverse post order).
- bool throwing_instruction_found = false;
- bool catch_block_visited = false;
- for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
- HBasicBlock* current = it.Current();
- if (current == block) {
- catch_block_visited = true;
- } else if (current->IsTryBlock() &&
- current->GetTryCatchInformation()->GetTryEntry().HasExceptionHandler(*block)) {
- DCHECK(!catch_block_visited) << "Catch block visited before its try block.";
- throwing_instruction_found |= current->HasThrowingInstructions();
- }
- }
- DCHECK(throwing_instruction_found) << "No instructions throwing into a live catch block.";
- }
- } else if (block->IsLoopHeader()) {
- // If the block is a loop header, we know we only have visited the pre header
- // because we are visiting in reverse post order. We create phis for all initialized
- // locals from the pre header. Their inputs will be populated at the end of
- // the analysis.
- for (size_t local = 0; local < current_locals_->size(); ++local) {
- HInstruction* incoming = ValueOfLocal(block->GetLoopInformation()->GetPreHeader(), local);
- if (incoming != nullptr) {
- HPhi* phi = new (GetGraph()->GetArena()) HPhi(
- GetGraph()->GetArena(),
- local,
- 0,
- incoming->GetType());
- block->AddPhi(phi);
- (*current_locals_)[local] = phi;
- }
- }
- // Save the loop header so that the last phase of the analysis knows which
- // blocks need to be updated.
- loop_headers_.push_back(block);
- } else if (block->GetPredecessors().size() > 0) {
- // All predecessors have already been visited because we are visiting in reverse post order.
- // We merge the values of all locals, creating phis if those values differ.
- for (size_t local = 0; local < current_locals_->size(); ++local) {
- bool one_predecessor_has_no_value = false;
- bool is_different = false;
- HInstruction* value = ValueOfLocal(block->GetPredecessors()[0], local);
-
- for (HBasicBlock* predecessor : block->GetPredecessors()) {
- HInstruction* current = ValueOfLocal(predecessor, local);
- if (current == nullptr) {
- one_predecessor_has_no_value = true;
- break;
- } else if (current != value) {
- is_different = true;
- }
- }
-
- if (one_predecessor_has_no_value) {
- // If one predecessor has no value for this local, we trust the verifier has
- // successfully checked that there is a store dominating any read after this block.
- continue;
- }
-
- if (is_different) {
- HInstruction* first_input = ValueOfLocal(block->GetPredecessors()[0], local);
- HPhi* phi = new (GetGraph()->GetArena()) HPhi(
- GetGraph()->GetArena(),
- local,
- block->GetPredecessors().size(),
- first_input->GetType());
- for (size_t i = 0; i < block->GetPredecessors().size(); i++) {
- HInstruction* pred_value = ValueOfLocal(block->GetPredecessors()[i], local);
- phi->SetRawInputAt(i, pred_value);
- }
- block->AddPhi(phi);
- value = phi;
- }
- (*current_locals_)[local] = value;
- }
- }
-
- // Visit all instructions. The instructions of interest are:
- // - HLoadLocal: replace them with the current value of the local.
- // - HStoreLocal: update current value of the local and remove the instruction.
- // - Instructions that require an environment: populate their environment
- // with the current values of the locals.
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- it.Current()->Accept(this);
- }
-}
-
/**
* Constants in the Dex format are not typed. So the builder types them as
* integers, but when doing the SSA form, we might realize the constant
@@ -723,11 +561,10 @@ HFloatConstant* SsaBuilder::GetFloatEquivalent(HIntConstant* constant) {
// We place the floating point constant next to this constant.
HFloatConstant* result = constant->GetNext()->AsFloatConstant();
if (result == nullptr) {
- HGraph* graph = constant->GetBlock()->GetGraph();
- ArenaAllocator* allocator = graph->GetArena();
- result = new (allocator) HFloatConstant(bit_cast<float, int32_t>(constant->GetValue()));
+ float value = bit_cast<float, int32_t>(constant->GetValue());
+ result = new (graph_->GetArena()) HFloatConstant(value);
constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext());
- graph->CacheFloatConstant(result);
+ graph_->CacheFloatConstant(result);
} else {
// If there is already a constant with the expected type, we know it is
// the floating point equivalent of this constant.
@@ -746,11 +583,10 @@ HDoubleConstant* SsaBuilder::GetDoubleEquivalent(HLongConstant* constant) {
// We place the floating point constant next to this constant.
HDoubleConstant* result = constant->GetNext()->AsDoubleConstant();
if (result == nullptr) {
- HGraph* graph = constant->GetBlock()->GetGraph();
- ArenaAllocator* allocator = graph->GetArena();
- result = new (allocator) HDoubleConstant(bit_cast<double, int64_t>(constant->GetValue()));
+ double value = bit_cast<double, int64_t>(constant->GetValue());
+ result = new (graph_->GetArena()) HDoubleConstant(value);
constant->GetBlock()->InsertInstructionBefore(result, constant->GetNext());
- graph->CacheDoubleConstant(result);
+ graph_->CacheDoubleConstant(result);
} else {
// If there is already a constant with the expected type, we know it is
// the floating point equivalent of this constant.
@@ -781,7 +617,7 @@ HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive:
if (next == nullptr
|| (next->AsPhi()->GetRegNumber() != phi->GetRegNumber())
|| (next->GetType() != type)) {
- ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena();
+ ArenaAllocator* allocator = graph_->GetArena();
HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type);
for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
// Copy the inputs. Note that the graph may not be correctly typed
@@ -841,7 +677,7 @@ HInstruction* SsaBuilder::GetFloatOrDoubleEquivalent(HInstruction* value, Primit
HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {
if (value->IsIntConstant() && value->AsIntConstant()->GetValue() == 0) {
- return value->GetBlock()->GetGraph()->GetNullConstant();
+ return graph_->GetNullConstant();
} else if (value->IsPhi()) {
return GetFloatDoubleOrReferenceEquivalentOfPhi(value->AsPhi(), Primitive::kPrimNot);
} else {
@@ -849,150 +685,4 @@ HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {
}
}
-void SsaBuilder::VisitLoadLocal(HLoadLocal* load) {
- Primitive::Type load_type = load->GetType();
- HInstruction* value = (*current_locals_)[load->GetLocal()->GetRegNumber()];
- // If the operation requests a specific type, we make sure its input is of that type.
- if (load_type != value->GetType()) {
- if (load_type == Primitive::kPrimFloat || load_type == Primitive::kPrimDouble) {
- value = GetFloatOrDoubleEquivalent(value, load_type);
- } else if (load_type == Primitive::kPrimNot) {
- value = GetReferenceTypeEquivalent(value);
- }
- }
-
- load->ReplaceWith(value);
- load->GetBlock()->RemoveInstruction(load);
-}
-
-void SsaBuilder::VisitStoreLocal(HStoreLocal* store) {
- uint32_t reg_number = store->GetLocal()->GetRegNumber();
- HInstruction* stored_value = store->InputAt(1);
- Primitive::Type stored_type = stored_value->GetType();
- DCHECK_NE(stored_type, Primitive::kPrimVoid);
-
- // Storing into vreg `reg_number` may implicitly invalidate the surrounding
- // registers. Consider the following cases:
- // (1) Storing a wide value must overwrite previous values in both `reg_number`
- // and `reg_number+1`. We store `nullptr` in `reg_number+1`.
- // (2) If vreg `reg_number-1` holds a wide value, writing into `reg_number`
- // must invalidate it. We store `nullptr` in `reg_number-1`.
- // Consequently, storing a wide value into the high vreg of another wide value
- // will invalidate both `reg_number-1` and `reg_number+1`.
-
- if (reg_number != 0) {
- HInstruction* local_low = (*current_locals_)[reg_number - 1];
- if (local_low != nullptr && Primitive::Is64BitType(local_low->GetType())) {
- // The vreg we are storing into was previously the high vreg of a pair.
- // We need to invalidate its low vreg.
- DCHECK((*current_locals_)[reg_number] == nullptr);
- (*current_locals_)[reg_number - 1] = nullptr;
- }
- }
-
- (*current_locals_)[reg_number] = stored_value;
- if (Primitive::Is64BitType(stored_type)) {
- // We are storing a pair. Invalidate the instruction in the high vreg.
- (*current_locals_)[reg_number + 1] = nullptr;
- }
-
- store->GetBlock()->RemoveInstruction(store);
-}
-
-void SsaBuilder::VisitInstruction(HInstruction* instruction) {
- if (instruction->NeedsEnvironment()) {
- HEnvironment* environment = new (GetGraph()->GetArena()) HEnvironment(
- GetGraph()->GetArena(),
- current_locals_->size(),
- GetGraph()->GetDexFile(),
- GetGraph()->GetMethodIdx(),
- instruction->GetDexPc(),
- GetGraph()->GetInvokeType(),
- instruction);
- environment->CopyFrom(*current_locals_);
- instruction->SetRawEnvironment(environment);
- }
-
- // If in a try block, propagate values of locals into catch blocks.
- if (instruction->CanThrowIntoCatchBlock()) {
- const HTryBoundary& try_entry =
- instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
- for (HBasicBlock* catch_block : try_entry.GetExceptionHandlers()) {
- ArenaVector<HInstruction*>* handler_locals = GetLocalsFor(catch_block);
- DCHECK_EQ(handler_locals->size(), current_locals_->size());
- for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
- HInstruction* handler_value = (*handler_locals)[vreg];
- if (handler_value == nullptr) {
- // Vreg was undefined at a previously encountered throwing instruction
- // and the catch phi was deleted. Do not record the local value.
- continue;
- }
- DCHECK(handler_value->IsPhi());
-
- HInstruction* local_value = (*current_locals_)[vreg];
- if (local_value == nullptr) {
- // This is the first instruction throwing into `catch_block` where
- // `vreg` is undefined. Delete the catch phi.
- catch_block->RemovePhi(handler_value->AsPhi());
- (*handler_locals)[vreg] = nullptr;
- } else {
- // Vreg has been defined at all instructions throwing into `catch_block`
- // encountered so far. Record the local value in the catch phi.
- handler_value->AsPhi()->AddInput(local_value);
- }
- }
- }
- }
-}
-
-void SsaBuilder::VisitArrayGet(HArrayGet* aget) {
- Primitive::Type type = aget->GetType();
- DCHECK(!Primitive::IsFloatingPointType(type));
- if (Primitive::IsIntOrLongType(type)) {
- ambiguous_agets_.push_back(aget);
- }
- VisitInstruction(aget);
-}
-
-void SsaBuilder::VisitArraySet(HArraySet* aset) {
- Primitive::Type type = aset->GetValue()->GetType();
- if (Primitive::IsIntOrLongType(type)) {
- ambiguous_asets_.push_back(aset);
- }
- VisitInstruction(aset);
-}
-
-void SsaBuilder::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
- VisitInstruction(invoke);
-
- if (invoke->IsStringInit()) {
- // This is a StringFactory call which acts as a String constructor. Its
- // result replaces the empty String pre-allocated by NewInstance.
- HInstruction* arg_this = invoke->GetAndRemoveThisArgumentOfStringInit();
-
- // Replacing the NewInstance might render it redundant. Keep a list of these
- // to be visited once it is clear whether it is has remaining uses.
- if (arg_this->IsNewInstance()) {
- HNewInstance* new_instance = arg_this->AsNewInstance();
- // Note that in some rare cases (b/27847265), the same NewInstance may be seen
- // multiple times. We should only consider it once for removal, so we
- // ensure it is not added more than once.
- if (!ContainsElement(uninitialized_strings_, new_instance)) {
- uninitialized_strings_.push_back(new_instance);
- }
- } else {
- DCHECK(arg_this->IsPhi());
- // NewInstance is not the direct input of the StringFactory call. It might
- // be redundant but optimizing this case is not worth the effort.
- }
-
- // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`.
- for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
- if ((*current_locals_)[vreg] == arg_this) {
- (*current_locals_)[vreg] = invoke;
- }
- }
- }
-}
-
} // namespace art
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 2dae9c2de0..c37c28c801 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -23,8 +23,6 @@
namespace art {
-static constexpr int kDefaultNumberOfLoops = 2;
-
/**
* Transforms a graph into SSA form. The liveness guarantees of
* this transformation are listed below. A DEX register
@@ -47,37 +45,48 @@ static constexpr int kDefaultNumberOfLoops = 2;
* is not set, values of Dex registers only used by environments
* are killed.
*/
-class SsaBuilder : public HGraphVisitor {
+class SsaBuilder : public ValueObject {
public:
SsaBuilder(HGraph* graph, StackHandleScopeCollection* handles)
- : HGraphVisitor(graph),
+ : graph_(graph),
handles_(handles),
agets_fixed_(false),
- current_locals_(nullptr),
- loop_headers_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
- ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
- ambiguous_asets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
- uninitialized_strings_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
- locals_for_(graph->GetBlocks().size(),
- ArenaVector<HInstruction*>(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
- graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) {
- loop_headers_.reserve(kDefaultNumberOfLoops);
+ ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
+ ambiguous_asets_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)),
+ uninitialized_strings_(graph->GetArena()->Adapter(kArenaAllocGraphBuilder)) {
+ graph_->InitializeInexactObjectRTI(handles);
}
GraphAnalysisResult BuildSsa();
- // Returns locals vector for `block`. If it is a catch block, the vector will be
- // prepopulated with catch phis for vregs which are defined in `current_locals_`.
- ArenaVector<HInstruction*>* GetLocalsFor(HBasicBlock* block);
- HInstruction* ValueOfLocal(HBasicBlock* block, size_t local);
+ HInstruction* GetFloatOrDoubleEquivalent(HInstruction* instruction, Primitive::Type type);
+ HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction);
+
+ void MaybeAddAmbiguousArrayGet(HArrayGet* aget) {
+ Primitive::Type type = aget->GetType();
+ DCHECK(!Primitive::IsFloatingPointType(type));
+ if (Primitive::IsIntOrLongType(type)) {
+ ambiguous_agets_.push_back(aget);
+ }
+ }
+
+ void MaybeAddAmbiguousArraySet(HArraySet* aset) {
+ Primitive::Type type = aset->GetValue()->GetType();
+ if (Primitive::IsIntOrLongType(type)) {
+ ambiguous_asets_.push_back(aset);
+ }
+ }
- void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
- void VisitLoadLocal(HLoadLocal* load) OVERRIDE;
- void VisitStoreLocal(HStoreLocal* store) OVERRIDE;
- void VisitInstruction(HInstruction* instruction) OVERRIDE;
- void VisitArrayGet(HArrayGet* aget) OVERRIDE;
- void VisitArraySet(HArraySet* aset) OVERRIDE;
- void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
+ void AddUninitializedString(HNewInstance* string) {
+ // In some rare cases (b/27847265), the same NewInstance may be seen
+ // multiple times. We should only consider it once for removal, so we
+ // ensure it is not added more than once.
+ // Note that we cannot check whether this really is a NewInstance of String
+ // before RTP. We DCHECK that in RemoveRedundantUninitializedStrings.
+ if (!ContainsElement(uninitialized_strings_, string)) {
+ uninitialized_strings_.push_back(string);
+ }
+ }
private:
void SetLoopHeaderPhiInputs();
@@ -95,9 +104,6 @@ class SsaBuilder : public HGraphVisitor {
bool UpdatePrimitiveType(HPhi* phi, ArenaVector<HPhi*>* worklist);
void ProcessPrimitiveTypePropagationWorklist(ArenaVector<HPhi*>* worklist);
- HInstruction* GetFloatOrDoubleEquivalent(HInstruction* instruction, Primitive::Type type);
- HInstruction* GetReferenceTypeEquivalent(HInstruction* instruction);
-
HFloatConstant* GetFloatEquivalent(HIntConstant* constant);
HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant);
HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);
@@ -105,25 +111,16 @@ class SsaBuilder : public HGraphVisitor {
void RemoveRedundantUninitializedStrings();
+ HGraph* graph_;
StackHandleScopeCollection* const handles_;
// True if types of ambiguous ArrayGets have been resolved.
bool agets_fixed_;
- // Locals for the current block being visited.
- ArenaVector<HInstruction*>* current_locals_;
-
- // Keep track of loop headers found. The last phase of the analysis iterates
- // over these blocks to set the inputs of their phis.
- ArenaVector<HBasicBlock*> loop_headers_;
-
ArenaVector<HArrayGet*> ambiguous_agets_;
ArenaVector<HArraySet*> ambiguous_asets_;
ArenaVector<HNewInstance*> uninitialized_strings_;
- // HEnvironment for each block.
- ArenaVector<ArenaVector<HInstruction*>> locals_for_;
-
DISALLOW_COPY_AND_ASSIGN(SsaBuilder);
};
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index a6880921c5..218bd53bc2 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -163,8 +163,8 @@ TEST_F(SsaTest, CFG3) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 0: IntConstant 0 [4, 4]\n"
- " 1: IntConstant 4 [8]\n"
- " 2: IntConstant 5 [8]\n"
+ " 1: IntConstant 5 [8]\n"
+ " 2: IntConstant 4 [8]\n"
" 3: Goto\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 4: Equal(0, 0) [5]\n"
@@ -174,7 +174,7 @@ TEST_F(SsaTest, CFG3) {
"BasicBlock 3, pred: 1, succ: 4\n"
" 7: Goto\n"
"BasicBlock 4, pred: 2, 3, succ: 5\n"
- " 8: Phi(1, 2) [9]\n"
+ " 8: Phi(2, 1) [9]\n"
" 9: Return(8)\n"
"BasicBlock 5, pred: 4\n"
" 10: Exit\n";
@@ -258,19 +258,19 @@ TEST_F(SsaTest, Loop3) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 0: IntConstant 0 [5]\n"
- " 1: IntConstant 4 [5]\n"
- " 2: IntConstant 5 [9]\n"
+ " 1: IntConstant 5 [9]\n"
+ " 2: IntConstant 4 [5]\n"
" 3: Goto\n"
"BasicBlock 1, pred: 0, succ: 2\n"
" 4: Goto\n"
"BasicBlock 2, pred: 1, 3, succ: 4, 3\n"
- " 5: Phi(0, 1) [6, 6]\n"
+ " 5: Phi(0, 2) [6, 6]\n"
" 6: Equal(5, 5) [7]\n"
" 7: If(6)\n"
"BasicBlock 3, pred: 2, succ: 2\n"
" 8: Goto\n"
"BasicBlock 4, pred: 2, succ: 5\n"
- " 9: Return(2)\n"
+ " 9: Return(1)\n"
"BasicBlock 5, pred: 4\n"
" 10: Exit\n";
@@ -326,8 +326,8 @@ TEST_F(SsaTest, Loop5) {
const char* expected =
"BasicBlock 0, succ: 1\n"
" 0: IntConstant 0 [4, 4]\n"
- " 1: IntConstant 4 [13]\n"
- " 2: IntConstant 5 [13]\n"
+ " 1: IntConstant 5 [13]\n"
+ " 2: IntConstant 4 [13]\n"
" 3: Goto\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 4: Equal(0, 0) [5]\n"
@@ -346,7 +346,7 @@ TEST_F(SsaTest, Loop5) {
"BasicBlock 7, pred: 6\n"
" 12: Exit\n"
"BasicBlock 8, pred: 2, 3, succ: 4\n"
- " 13: Phi(1, 2) [8, 8, 11]\n"
+ " 13: Phi(2, 1) [8, 8, 11]\n"
" 14: Goto\n";
const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
@@ -496,7 +496,7 @@ TEST_F(SsaTest, MultiplePredecessors) {
// does not update the local.
const char* expected =
"BasicBlock 0, succ: 1\n"
- " 0: IntConstant 0 [4, 8, 6, 6, 2, 2, 8, 4]\n"
+ " 0: IntConstant 0 [4, 4, 8, 8, 6, 6, 2, 2]\n"
" 1: Goto\n"
"BasicBlock 1, pred: 0, succ: 3, 2\n"
" 2: Equal(0, 0) [3]\n"
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index c571312faa..11a254ef63 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -137,8 +137,9 @@ uint32_t StackMapStream::ComputeMaxNativePcOffset() const {
size_t StackMapStream::PrepareForFillIn() {
int stack_mask_number_of_bits = stack_mask_max_ + 1; // Need room for max element too.
- inline_info_size_ = ComputeInlineInfoSize();
dex_register_maps_size_ = ComputeDexRegisterMapsSize();
+ ComputeInlineInfoEncoding(); // needs dex_register_maps_size_.
+ inline_info_size_ = inline_infos_.size() * inline_info_encoding_.GetEntrySize();
uint32_t max_native_pc_offset = ComputeMaxNativePcOffset();
size_t stack_map_size = stack_map_encoding_.SetFromSizes(max_native_pc_offset,
dex_pc_max_,
@@ -158,9 +159,10 @@ size_t StackMapStream::PrepareForFillIn() {
// Prepare the CodeInfo variable-sized encoding.
CodeInfoEncoding code_info_encoding;
code_info_encoding.non_header_size = non_header_size;
- code_info_encoding.stack_map_encoding = stack_map_encoding_;
code_info_encoding.number_of_stack_maps = stack_maps_.size();
code_info_encoding.stack_map_size_in_bytes = stack_map_size;
+ code_info_encoding.stack_map_encoding = stack_map_encoding_;
+ code_info_encoding.inline_info_encoding = inline_info_encoding_;
code_info_encoding.number_of_location_catalog_entries = location_catalog_entries_.size();
code_info_encoding.Compress(&code_info_encoding_);
@@ -224,10 +226,26 @@ size_t StackMapStream::ComputeDexRegisterMapsSize() const {
return size;
}
-size_t StackMapStream::ComputeInlineInfoSize() const {
- return inline_infos_.size() * InlineInfo::SingleEntrySize()
- // For encoding the depth.
- + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize);
+void StackMapStream::ComputeInlineInfoEncoding() {
+ uint32_t method_index_max = 0;
+ uint32_t dex_pc_max = 0;
+ uint32_t invoke_type_max = 0;
+
+ uint32_t inline_info_index = 0;
+ for (const StackMapEntry& entry : stack_maps_) {
+ for (size_t j = 0; j < entry.inlining_depth; ++j) {
+ InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
+ method_index_max = std::max(method_index_max, inline_entry.method_index);
+ dex_pc_max = std::max(dex_pc_max, inline_entry.dex_pc);
+ invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type));
+ }
+ }
+ DCHECK_EQ(inline_info_index, inline_infos_.size());
+
+ inline_info_encoding_.SetFromSizes(method_index_max,
+ dex_pc_max,
+ invoke_type_max,
+ dex_register_maps_size_);
}
void StackMapStream::FillIn(MemoryRegion region) {
@@ -321,7 +339,7 @@ void StackMapStream::FillIn(MemoryRegion region) {
if (entry.inlining_depth != 0) {
MemoryRegion inline_region = inline_infos_region.Subregion(
next_inline_info_offset,
- InlineInfo::kFixedSize + entry.inlining_depth * InlineInfo::SingleEntrySize());
+ entry.inlining_depth * inline_info_encoding_.GetEntrySize());
next_inline_info_offset += inline_region.size();
InlineInfo inline_info(inline_region);
@@ -329,16 +347,18 @@ void StackMapStream::FillIn(MemoryRegion region) {
stack_map.SetInlineDescriptorOffset(
stack_map_encoding_, inline_region.start() - dex_register_locations_region.start());
- inline_info.SetDepth(entry.inlining_depth);
+ inline_info.SetDepth(inline_info_encoding_, entry.inlining_depth);
DCHECK_LE(entry.inline_infos_start_index + entry.inlining_depth, inline_infos_.size());
for (size_t depth = 0; depth < entry.inlining_depth; ++depth) {
InlineInfoEntry inline_entry = inline_infos_[depth + entry.inline_infos_start_index];
- inline_info.SetMethodIndexAtDepth(depth, inline_entry.method_index);
- inline_info.SetDexPcAtDepth(depth, inline_entry.dex_pc);
- inline_info.SetInvokeTypeAtDepth(depth, inline_entry.invoke_type);
+ inline_info.SetMethodIndexAtDepth(inline_info_encoding_, depth, inline_entry.method_index);
+ inline_info.SetDexPcAtDepth(inline_info_encoding_, depth, inline_entry.dex_pc);
+ inline_info.SetInvokeTypeAtDepth(inline_info_encoding_, depth, inline_entry.invoke_type);
if (inline_entry.num_dex_registers == 0) {
// No dex map available.
- inline_info.SetDexRegisterMapOffsetAtDepth(depth, StackMap::kNoDexRegisterMap);
+ inline_info.SetDexRegisterMapOffsetAtDepth(inline_info_encoding_,
+ depth,
+ StackMap::kNoDexRegisterMap);
DCHECK(inline_entry.live_dex_registers_mask == nullptr);
} else {
MemoryRegion register_region = dex_register_locations_region.Subregion(
@@ -348,7 +368,8 @@ void StackMapStream::FillIn(MemoryRegion region) {
next_dex_register_map_offset += register_region.size();
DexRegisterMap dex_register_map(register_region);
inline_info.SetDexRegisterMapOffsetAtDepth(
- depth, register_region.start() - dex_register_locations_region.start());
+ inline_info_encoding_,
+ depth, register_region.start() - dex_register_locations_region.start());
FillInDexRegisterMap(dex_register_map,
inline_entry.num_dex_registers,
@@ -513,14 +534,17 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const {
DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0));
if (entry.inlining_depth != 0) {
InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
- DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth);
+ DCHECK_EQ(inline_info.GetDepth(encoding.inline_info_encoding), entry.inlining_depth);
for (size_t d = 0; d < entry.inlining_depth; ++d) {
size_t inline_info_index = entry.inline_infos_start_index + d;
DCHECK_LT(inline_info_index, inline_infos_.size());
InlineInfoEntry inline_entry = inline_infos_[inline_info_index];
- DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc);
- DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index);
- DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type);
+ DCHECK_EQ(inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, d),
+ inline_entry.dex_pc);
+ DCHECK_EQ(inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, d),
+ inline_entry.method_index);
+ DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, d),
+ inline_entry.invoke_type);
CheckDexRegisterMap(code_info,
code_info.GetDexRegisterMapAtDepth(
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index b686748802..41f72f508b 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -156,7 +156,7 @@ class StackMapStream : public ValueObject {
size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers,
const BitVector* live_dex_registers_mask) const;
size_t ComputeDexRegisterMapsSize() const;
- size_t ComputeInlineInfoSize() const;
+ void ComputeInlineInfoEncoding();
// Returns the index of an entry with the same dex register map as the current_entry,
// or kNoSameDexMapFound if no such entry exists.
@@ -200,6 +200,7 @@ class StackMapStream : public ValueObject {
StackMapEntry current_entry_;
InlineInfoEntry current_inline_info_;
StackMapEncoding stack_map_encoding_;
+ InlineInfoEncoding inline_info_encoding_;
ArenaVector<uint8_t> code_info_encoding_;
size_t inline_info_size_;
size_t dex_register_maps_size_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index 35524877e3..967fd96561 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -237,13 +237,13 @@ TEST(StackMapTest, Test2) {
ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map_encoding));
InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
- ASSERT_EQ(2u, inline_info.GetDepth());
- ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(0));
- ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(1));
- ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(0));
- ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(1));
- ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(0));
- ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(1));
+ ASSERT_EQ(2u, inline_info.GetDepth(encoding.inline_info_encoding));
+ ASSERT_EQ(82u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(42u, inline_info.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(3u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(2u, inline_info.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(kDirect, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(kStatic, inline_info.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
}
// Second stack map.
@@ -741,13 +741,13 @@ TEST(StackMapTest, InlineTest) {
ASSERT_EQ(4, dex_registers0.GetConstant(1, 2, ci, encoding));
InlineInfo if0 = ci.GetInlineInfoOf(sm0, encoding);
- ASSERT_EQ(2u, if0.GetDepth());
- ASSERT_EQ(2u, if0.GetDexPcAtDepth(0));
- ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(0));
- ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(0));
- ASSERT_EQ(3u, if0.GetDexPcAtDepth(1));
- ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(1));
- ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(1));
+ ASSERT_EQ(2u, if0.GetDepth(encoding.inline_info_encoding));
+ ASSERT_EQ(2u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(42u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(3u, if0.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(82u, if0.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(kStatic, if0.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if0, encoding, 1);
ASSERT_EQ(8, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
@@ -767,16 +767,16 @@ TEST(StackMapTest, InlineTest) {
ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding));
InlineInfo if1 = ci.GetInlineInfoOf(sm1, encoding);
- ASSERT_EQ(3u, if1.GetDepth());
- ASSERT_EQ(2u, if1.GetDexPcAtDepth(0));
- ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(0));
- ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(0));
- ASSERT_EQ(3u, if1.GetDexPcAtDepth(1));
- ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(1));
- ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(1));
- ASSERT_EQ(5u, if1.GetDexPcAtDepth(2));
- ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(2));
- ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(2));
+ ASSERT_EQ(3u, if1.GetDepth(encoding.inline_info_encoding));
+ ASSERT_EQ(2u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(42u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(kDirect, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(3u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(82u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(kStatic, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(5u, if1.GetDexPcAtDepth(encoding.inline_info_encoding, 2));
+ ASSERT_EQ(52u, if1.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2));
+ ASSERT_EQ(kVirtual, if1.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2));
DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(0, if1, encoding, 1);
ASSERT_EQ(12, dex_registers1.GetStackOffsetInBytes(0, 1, ci, encoding));
@@ -786,7 +786,7 @@ TEST(StackMapTest, InlineTest) {
ASSERT_EQ(10, dex_registers2.GetConstant(1, 3, ci, encoding));
ASSERT_EQ(5, dex_registers2.GetMachineRegister(2, 3, ci, encoding));
- ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(2));
+ ASSERT_FALSE(if1.HasDexRegisterMapAtDepth(encoding.inline_info_encoding, 2));
}
{
@@ -808,18 +808,18 @@ TEST(StackMapTest, InlineTest) {
ASSERT_EQ(0, dex_registers0.GetConstant(1, 2, ci, encoding));
InlineInfo if2 = ci.GetInlineInfoOf(sm3, encoding);
- ASSERT_EQ(3u, if2.GetDepth());
- ASSERT_EQ(2u, if2.GetDexPcAtDepth(0));
- ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(0));
- ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(0));
- ASSERT_EQ(5u, if2.GetDexPcAtDepth(1));
- ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(1));
- ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(1));
- ASSERT_EQ(10u, if2.GetDexPcAtDepth(2));
- ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(2));
- ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(2));
-
- ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(0));
+ ASSERT_EQ(3u, if2.GetDepth(encoding.inline_info_encoding));
+ ASSERT_EQ(2u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(42u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(kVirtual, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 0));
+ ASSERT_EQ(5u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(kInterface, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 1));
+ ASSERT_EQ(10u, if2.GetDexPcAtDepth(encoding.inline_info_encoding, 2));
+ ASSERT_EQ(52u, if2.GetMethodIndexAtDepth(encoding.inline_info_encoding, 2));
+ ASSERT_EQ(kStatic, if2.GetInvokeTypeAtDepth(encoding.inline_info_encoding, 2));
+
+ ASSERT_FALSE(if2.HasDexRegisterMapAtDepth(encoding.inline_info_encoding, 0));
DexRegisterMap dex_registers1 = ci.GetDexRegisterMapAtDepth(1, if2, encoding, 1);
ASSERT_EQ(2, dex_registers1.GetMachineRegister(0, 1, ci, encoding));