diff options
169 files changed, 5928 insertions, 1593 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 7cb23f8f19..7b38e5ed4c 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -244,7 +244,7 @@ art_target_non_debug_cflags := $(art_non_debug_cflags) ifeq ($(HOST_OS),linux) # Larger frame-size for host clang builds today ifndef SANITIZE_HOST - art_host_non_debug_cflags += -Wframe-larger-than=2600 + art_host_non_debug_cflags += -Wframe-larger-than=2700 endif art_target_non_debug_cflags += -Wframe-larger-than=1728 endif @@ -252,7 +252,7 @@ endif ifndef LIBART_IMG_HOST_BASE_ADDRESS $(error LIBART_IMG_HOST_BASE_ADDRESS unset) endif -ART_HOST_CFLAGS += $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) +ART_HOST_CFLAGS += $(art_cflags) -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default ifndef LIBART_IMG_TARGET_BASE_ADDRESS @@ -283,18 +283,6 @@ endif ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA) ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA) -ifeq ($(TARGET_CPU_SMP),true) - ART_TARGET_CFLAGS += -DANDROID_SMP=1 -else - ifeq ($(TARGET_CPU_SMP),false) - ART_TARGET_CFLAGS += -DANDROID_SMP=0 - else - $(warning TARGET_CPU_SMP should be (true|false), found $(TARGET_CPU_SMP)) - # Make sure we emit barriers for the worst case. - ART_TARGET_CFLAGS += -DANDROID_SMP=1 - endif -endif - # To use oprofile_android --callgraph, uncomment this and recompile with "mmm art -B -j16" # ART_TARGET_CFLAGS += -fno-omit-frame-pointer -marm -mapcs diff --git a/compiler/Android.mk b/compiler/Android.mk index 610f453816..eb9ad475b4 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -178,6 +178,7 @@ LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ dex/pass_me.h \ driver/compiler_driver.h \ driver/compiler_options.h \ + image_writer.h \ optimizing/locations.h \ utils/arm/constants_arm.h @@ -206,7 +207,9 @@ define build-libart-compiler ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := libart-compiler LOCAL_SHARED_LIBRARIES += libart - LOCAL_FDO_SUPPORT := true + ifeq ($$(art_target_or_host),target) + LOCAL_FDO_SUPPORT := true + endif else # debug LOCAL_MODULE := libartd-compiler LOCAL_SHARED_LIBRARIES += libartd diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 5d877fdf80..b56fd6f5c7 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -610,21 +610,22 @@ std::ostream& operator<<(std::ostream& os, const SelectInstructionKind& kind); // LIR fixup kinds for Arm enum FixupKind { kFixupNone, - kFixupLabel, // For labels we just adjust the offset. - kFixupLoad, // Mostly for immediates. - kFixupVLoad, // FP load which *may* be pc-relative. - kFixupCBxZ, // Cbz, Cbnz. - kFixupTBxZ, // Tbz, Tbnz. - kFixupPushPop, // Not really pc relative, but changes size based on args. - kFixupCondBranch, // Conditional branch - kFixupT1Branch, // Thumb1 Unconditional branch - kFixupT2Branch, // Thumb2 Unconditional branch - kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair). - kFixupBl1, // Bl1 (start of Bl1/Bl2 pair). - kFixupAdr, // Adr. - kFixupMovImmLST, // kThumb2MovImm16LST. - kFixupMovImmHST, // kThumb2MovImm16HST. - kFixupAlign4, // Align to 4-byte boundary. + kFixupLabel, // For labels we just adjust the offset. + kFixupLoad, // Mostly for immediates. + kFixupVLoad, // FP load which *may* be pc-relative. + kFixupCBxZ, // Cbz, Cbnz. + kFixupTBxZ, // Tbz, Tbnz. + kFixupPushPop, // Not really pc relative, but changes size based on args. + kFixupCondBranch, // Conditional branch + kFixupT1Branch, // Thumb1 Unconditional branch + kFixupT2Branch, // Thumb2 Unconditional branch + kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair). + kFixupBl1, // Bl1 (start of Bl1/Bl2 pair). + kFixupAdr, // Adr. + kFixupMovImmLST, // kThumb2MovImm16LST. + kFixupMovImmHST, // kThumb2MovImm16HST. + kFixupAlign4, // Align to 4-byte boundary. + kFixupA53Erratum835769, // Cortex A53 Erratum 835769. }; std::ostream& operator<<(std::ostream& os, const FixupKind& kind); diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index b87ab66347..29972ddba5 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -97,11 +97,6 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) max_nested_loops_(0u), i_dom_list_(NULL), temp_scoped_alloc_(), - temp_insn_data_(nullptr), - temp_bit_vector_size_(0u), - temp_bit_vector_(nullptr), - temp_bit_matrix_(nullptr), - temp_gvn_(), block_list_(arena->Adapter(kArenaAllocBBList)), try_block_addr_(NULL), entry_block_(NULL), @@ -133,6 +128,7 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) sfield_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), method_lowering_infos_(arena->Adapter(kArenaAllocLoweringInfo)), gen_suspend_test_list_(arena->Adapter()) { + memset(&temp_, 0, sizeof(temp_)); use_counts_.reserve(256); raw_use_counts_.reserve(256); block_list_.reserve(100); @@ -1348,9 +1344,10 @@ void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir decoded_mir->append(", "); decoded_mir->append(GetSSANameWithConst(ssa_rep->defs[1], false)); } - decoded_mir->append(StringPrintf(" = vect%d", mir->dalvikInsn.vB)); + decoded_mir->append(StringPrintf(" = vect%d (extr_idx:%d)", mir->dalvikInsn.vB, mir->dalvikInsn.arg[0])); } else { - decoded_mir->append(StringPrintf(" v%d = vect%d", mir->dalvikInsn.vA, mir->dalvikInsn.vB)); + decoded_mir->append(StringPrintf(" v%d = vect%d (extr_idx:%d)", mir->dalvikInsn.vA, + mir->dalvikInsn.vB, mir->dalvikInsn.arg[0])); } FillTypeSizeString(mir->dalvikInsn.vC, decoded_mir); break; @@ -1681,9 +1678,9 @@ void MIRGraph::InitializeMethodUses() { void MIRGraph::SSATransformationStart() { DCHECK(temp_scoped_alloc_.get() == nullptr); temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_bit_vector_size_ = GetNumOfCodeAndTempVRs(); - temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapRegisterV); + temp_.ssa.num_vregs = GetNumOfCodeAndTempVRs(); + temp_.ssa.work_live_vregs = new (temp_scoped_alloc_.get()) ArenaBitVector( + temp_scoped_alloc_.get(), temp_.ssa.num_vregs, false, kBitMapRegisterV); } void MIRGraph::SSATransformationEnd() { @@ -1692,9 +1689,9 @@ void MIRGraph::SSATransformationEnd() { VerifyDataflow(); } - temp_bit_vector_size_ = 0u; - temp_bit_vector_ = nullptr; - temp_bit_matrix_ = nullptr; // Def block matrix. + temp_.ssa.num_vregs = 0u; + temp_.ssa.work_live_vregs = nullptr; + temp_.ssa.def_block_matrix = nullptr; DCHECK(temp_scoped_alloc_.get() != nullptr); temp_scoped_alloc_.reset(); diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index a1d24e2c37..d77ad6f76a 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -1270,15 +1270,38 @@ class MIRGraph { size_t max_nested_loops_; int* i_dom_list_; std::unique_ptr<ScopedArenaAllocator> temp_scoped_alloc_; - uint16_t* temp_insn_data_; - uint32_t temp_bit_vector_size_; - ArenaBitVector* temp_bit_vector_; - // temp_bit_matrix_ used as one of - // - def_block_matrix: original num registers x num_blocks_, - // - ending_null_check_matrix: num_blocks_ x original num registers, - // - ending_clinit_check_matrix: num_blocks_ x unique class count. - ArenaBitVector** temp_bit_matrix_; - std::unique_ptr<GlobalValueNumbering> temp_gvn_; + // Union of temporaries used by different passes. + union { + // Class init check elimination. + struct { + size_t num_class_bits; // 2 bits per class: class initialized and class in dex cache. + ArenaBitVector* work_classes_to_check; + ArenaBitVector** ending_classes_to_check_matrix; // num_blocks_ x num_class_bits. + uint16_t* indexes; + } cice; + // Null check elimination. + struct { + size_t num_vregs; + ArenaBitVector* work_vregs_to_check; + ArenaBitVector** ending_vregs_to_check_matrix; // num_blocks_ x num_vregs. + } nce; + // Special method inlining. + struct { + size_t num_indexes; + ArenaBitVector* processed_indexes; + uint16_t* lowering_infos; + } smi; + // SSA transformation. + struct { + size_t num_vregs; + ArenaBitVector* work_live_vregs; + ArenaBitVector** def_block_matrix; // num_vregs x num_blocks_. + } ssa; + // Global value numbering. + struct { + GlobalValueNumbering* gvn; + } gvn; + } temp_; static const int kInvalidEntry = -1; ArenaVector<BasicBlock*> block_list_; ArenaBitVector* try_block_addr_; diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index a0ad2133be..d025d08168 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -891,12 +891,12 @@ bool MIRGraph::EliminateNullChecksGate() { DCHECK(temp_scoped_alloc_.get() == nullptr); temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_bit_vector_size_ = GetNumOfCodeVRs(); - temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck); - temp_bit_matrix_ = static_cast<ArenaBitVector**>( + temp_.nce.num_vregs = GetNumOfCodeVRs(); + temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( + temp_scoped_alloc_.get(), temp_.nce.num_vregs, false, kBitMapNullCheck); + temp_.nce.ending_vregs_to_check_matrix = static_cast<ArenaBitVector**>( temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc)); - std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr); + std::fill_n(temp_.nce.ending_vregs_to_check_matrix, GetNumBlocks(), nullptr); // reset MIR_MARK AllNodesIterator iter(this); @@ -919,7 +919,7 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { return false; } - ArenaBitVector* vregs_to_check = temp_bit_vector_; + ArenaBitVector* vregs_to_check = temp_.nce.work_vregs_to_check; /* * Set initial state. Catch blocks don't need any special treatment. */ @@ -940,7 +940,7 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { // Starting state is union of all incoming arcs. bool copied_first = false; for (BasicBlockId pred_id : bb->predecessors) { - if (temp_bit_matrix_[pred_id] == nullptr) { + if (temp_.nce.ending_vregs_to_check_matrix[pred_id] == nullptr) { continue; } BasicBlock* pred_bb = GetBasicBlock(pred_id); @@ -962,9 +962,9 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { } if (!copied_first) { copied_first = true; - vregs_to_check->Copy(temp_bit_matrix_[pred_id]); + vregs_to_check->Copy(temp_.nce.ending_vregs_to_check_matrix[pred_id]); } else { - vregs_to_check->Union(temp_bit_matrix_[pred_id]); + vregs_to_check->Union(temp_.nce.ending_vregs_to_check_matrix[pred_id]); } if (null_check_insn != nullptr) { vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA); @@ -1057,27 +1057,27 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { // Did anything change? bool nce_changed = false; - ArenaBitVector* old_ending_ssa_regs_to_check = temp_bit_matrix_[bb->id]; + ArenaBitVector* old_ending_ssa_regs_to_check = temp_.nce.ending_vregs_to_check_matrix[bb->id]; if (old_ending_ssa_regs_to_check == nullptr) { DCHECK(temp_scoped_alloc_.get() != nullptr); nce_changed = vregs_to_check->GetHighestBitSet() != -1; - temp_bit_matrix_[bb->id] = vregs_to_check; + temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; // Create a new vregs_to_check for next BB. - temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck); + temp_.nce.work_vregs_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( + temp_scoped_alloc_.get(), temp_.nce.num_vregs, false, kBitMapNullCheck); } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) { nce_changed = true; - temp_bit_matrix_[bb->id] = vregs_to_check; - temp_bit_vector_ = old_ending_ssa_regs_to_check; // Reuse for vregs_to_check for next BB. + temp_.nce.ending_vregs_to_check_matrix[bb->id] = vregs_to_check; + temp_.nce.work_vregs_to_check = old_ending_ssa_regs_to_check; // Reuse for next BB. } return nce_changed; } void MIRGraph::EliminateNullChecksEnd() { // Clean up temporaries. - temp_bit_vector_size_ = 0u; - temp_bit_vector_ = nullptr; - temp_bit_matrix_ = nullptr; + temp_.nce.num_vregs = 0u; + temp_.nce.work_vregs_to_check = nullptr; + temp_.nce.ending_vregs_to_check_matrix = nullptr; DCHECK(temp_scoped_alloc_.get() != nullptr); temp_scoped_alloc_.reset(); @@ -1124,9 +1124,9 @@ bool MIRGraph::EliminateClassInitChecksGate() { // Each insn we use here has at least 2 code units, offset/2 will be a unique index. const size_t end = (GetNumDalvikInsns() + 1u) / 2u; - temp_insn_data_ = static_cast<uint16_t*>( - temp_scoped_alloc_->Alloc(end * sizeof(*temp_insn_data_), kArenaAllocGrowableArray)); - std::fill_n(temp_insn_data_, end, 0xffffu); + temp_.cice.indexes = static_cast<uint16_t*>( + temp_scoped_alloc_->Alloc(end * sizeof(*temp_.cice.indexes), kArenaAllocGrowableArray)); + std::fill_n(temp_.cice.indexes, end, 0xffffu); uint32_t unique_class_count = 0u; { @@ -1173,8 +1173,8 @@ bool MIRGraph::EliminateClassInitChecksGate() { static_cast<uint16_t>(class_to_index_map.size()) }; uint16_t index = class_to_index_map.insert(entry).first->index; - // Using offset/2 for index into temp_insn_data_. - temp_insn_data_[mir->offset / 2u] = index; + // Using offset/2 for index into temp_.cice.indexes. + temp_.cice.indexes[mir->offset / 2u] = index; } } else if (mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC || mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE) { @@ -1187,8 +1187,8 @@ bool MIRGraph::EliminateClassInitChecksGate() { static_cast<uint16_t>(class_to_index_map.size()) }; uint16_t index = class_to_index_map.insert(entry).first->index; - // Using offset/2 for index into temp_insn_data_. - temp_insn_data_[mir->offset / 2u] = index; + // Using offset/2 for index into temp_.cice.indexes. + temp_.cice.indexes[mir->offset / 2u] = index; } } } @@ -1199,19 +1199,19 @@ bool MIRGraph::EliminateClassInitChecksGate() { if (unique_class_count == 0u) { // All SGET/SPUTs refer to initialized classes. Nothing to do. - temp_insn_data_ = nullptr; + temp_.cice.indexes = nullptr; temp_scoped_alloc_.reset(); return false; } // 2 bits for each class: is class initialized, is class in dex cache. - temp_bit_vector_size_ = 2u * unique_class_count; - temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck); - temp_bit_matrix_ = static_cast<ArenaBitVector**>( + temp_.cice.num_class_bits = 2u * unique_class_count; + temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( + temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false, kBitMapClInitCheck); + temp_.cice.ending_classes_to_check_matrix = static_cast<ArenaBitVector**>( temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * GetNumBlocks(), kArenaAllocMisc)); - std::fill_n(temp_bit_matrix_, GetNumBlocks(), nullptr); - DCHECK_GT(temp_bit_vector_size_, 0u); + std::fill_n(temp_.cice.ending_classes_to_check_matrix, GetNumBlocks(), nullptr); + DCHECK_GT(temp_.cice.num_class_bits, 0u); return true; } @@ -1229,22 +1229,22 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { /* * Set initial state. Catch blocks don't need any special treatment. */ - ArenaBitVector* classes_to_check = temp_bit_vector_; + ArenaBitVector* classes_to_check = temp_.cice.work_classes_to_check; DCHECK(classes_to_check != nullptr); if (bb->block_type == kEntryBlock) { - classes_to_check->SetInitialBits(temp_bit_vector_size_); + classes_to_check->SetInitialBits(temp_.cice.num_class_bits); } else { // Starting state is union of all incoming arcs. bool copied_first = false; for (BasicBlockId pred_id : bb->predecessors) { - if (temp_bit_matrix_[pred_id] == nullptr) { + if (temp_.cice.ending_classes_to_check_matrix[pred_id] == nullptr) { continue; } if (!copied_first) { copied_first = true; - classes_to_check->Copy(temp_bit_matrix_[pred_id]); + classes_to_check->Copy(temp_.cice.ending_classes_to_check_matrix[pred_id]); } else { - classes_to_check->Union(temp_bit_matrix_[pred_id]); + classes_to_check->Union(temp_.cice.ending_classes_to_check_matrix[pred_id]); } } DCHECK(copied_first); // At least one predecessor must have been processed before this bb. @@ -1253,7 +1253,7 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { // Walk through the instruction in the block, updating as necessary for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - uint16_t index = temp_insn_data_[mir->offset / 2u]; + uint16_t index = temp_.cice.indexes[mir->offset / 2u]; if (index != 0xffffu) { bool check_initialization = false; bool check_dex_cache = false; @@ -1299,29 +1299,29 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { // Did anything change? bool changed = false; - ArenaBitVector* old_ending_classes_to_check = temp_bit_matrix_[bb->id]; + ArenaBitVector* old_ending_classes_to_check = temp_.cice.ending_classes_to_check_matrix[bb->id]; if (old_ending_classes_to_check == nullptr) { DCHECK(temp_scoped_alloc_.get() != nullptr); changed = classes_to_check->GetHighestBitSet() != -1; - temp_bit_matrix_[bb->id] = classes_to_check; + temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; // Create a new classes_to_check for next BB. - temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapClInitCheck); + temp_.cice.work_classes_to_check = new (temp_scoped_alloc_.get()) ArenaBitVector( + temp_scoped_alloc_.get(), temp_.cice.num_class_bits, false, kBitMapClInitCheck); } else if (!classes_to_check->Equal(old_ending_classes_to_check)) { changed = true; - temp_bit_matrix_[bb->id] = classes_to_check; - temp_bit_vector_ = old_ending_classes_to_check; // Reuse for classes_to_check for next BB. + temp_.cice.ending_classes_to_check_matrix[bb->id] = classes_to_check; + temp_.cice.work_classes_to_check = old_ending_classes_to_check; // Reuse for next BB. } return changed; } void MIRGraph::EliminateClassInitChecksEnd() { // Clean up temporaries. - temp_bit_vector_size_ = 0u; - temp_bit_vector_ = nullptr; - temp_bit_matrix_ = nullptr; - DCHECK(temp_insn_data_ != nullptr); - temp_insn_data_ = nullptr; + temp_.cice.num_class_bits = 0u; + temp_.cice.work_classes_to_check = nullptr; + temp_.cice.ending_classes_to_check_matrix = nullptr; + DCHECK(temp_.cice.indexes != nullptr); + temp_.cice.indexes = nullptr; DCHECK(temp_scoped_alloc_.get() != nullptr); temp_scoped_alloc_.reset(); } @@ -1333,39 +1333,39 @@ bool MIRGraph::ApplyGlobalValueNumberingGate() { DCHECK(temp_scoped_alloc_ == nullptr); temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - DCHECK(temp_gvn_ == nullptr); - temp_gvn_.reset( - new (temp_scoped_alloc_.get()) GlobalValueNumbering(cu_, temp_scoped_alloc_.get(), - GlobalValueNumbering::kModeGvn)); + DCHECK(temp_.gvn.gvn == nullptr); + temp_.gvn.gvn = new (temp_scoped_alloc_.get()) GlobalValueNumbering( + cu_, temp_scoped_alloc_.get(), GlobalValueNumbering::kModeGvn); return true; } bool MIRGraph::ApplyGlobalValueNumbering(BasicBlock* bb) { - DCHECK(temp_gvn_ != nullptr); - LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb); + DCHECK(temp_.gvn.gvn != nullptr); + LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb); if (lvn != nullptr) { for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { lvn->GetValueNumber(mir); } } - bool change = (lvn != nullptr) && temp_gvn_->FinishBasicBlock(bb); + bool change = (lvn != nullptr) && temp_.gvn.gvn->FinishBasicBlock(bb); return change; } void MIRGraph::ApplyGlobalValueNumberingEnd() { // Perform modifications. - if (temp_gvn_->Good()) { + DCHECK(temp_.gvn.gvn != nullptr); + if (temp_.gvn.gvn->Good()) { if (max_nested_loops_ != 0u) { - temp_gvn_->StartPostProcessing(); + temp_.gvn.gvn->StartPostProcessing(); TopologicalSortIterator iter(this); for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { ScopedArenaAllocator allocator(&cu_->arena_stack); // Reclaim memory after each LVN. - LocalValueNumbering* lvn = temp_gvn_->PrepareBasicBlock(bb, &allocator); + LocalValueNumbering* lvn = temp_.gvn.gvn->PrepareBasicBlock(bb, &allocator); if (lvn != nullptr) { for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { lvn->GetValueNumber(mir); } - bool change = temp_gvn_->FinishBasicBlock(bb); + bool change = temp_.gvn.gvn->FinishBasicBlock(bb); DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file); } } @@ -1376,16 +1376,16 @@ void MIRGraph::ApplyGlobalValueNumberingEnd() { LOG(WARNING) << "GVN failed for " << PrettyMethod(cu_->method_idx, *cu_->dex_file); } - DCHECK(temp_gvn_ != nullptr); - temp_gvn_.reset(); + delete temp_.gvn.gvn; + temp_.gvn.gvn = nullptr; DCHECK(temp_scoped_alloc_ != nullptr); temp_scoped_alloc_.reset(); } void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, MIR* iget_or_iput) { uint32_t method_index = invoke->meta.method_lowering_info; - if (temp_bit_vector_->IsBitSet(method_index)) { - iget_or_iput->meta.ifield_lowering_info = temp_insn_data_[method_index]; + if (temp_.smi.processed_indexes->IsBitSet(method_index)) { + iget_or_iput->meta.ifield_lowering_info = temp_.smi.lowering_infos[method_index]; DCHECK_EQ(field_idx, GetIFieldLoweringInfo(iget_or_iput).FieldIndex()); return; } @@ -1402,8 +1402,8 @@ void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, uint32_t field_info_index = ifield_lowering_infos_.size(); ifield_lowering_infos_.push_back(inlined_field_info); - temp_bit_vector_->SetBit(method_index); - temp_insn_data_[method_index] = field_info_index; + temp_.smi.processed_indexes->SetBit(method_index); + temp_.smi.lowering_infos[method_index] = field_info_index; iget_or_iput->meta.ifield_lowering_info = field_info_index; } @@ -1425,12 +1425,12 @@ void MIRGraph::InlineSpecialMethodsStart() { DCHECK(temp_scoped_alloc_.get() == nullptr); temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); - temp_bit_vector_size_ = method_lowering_infos_.size(); - temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector( - temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapMisc); - temp_bit_vector_->ClearAllBits(); - temp_insn_data_ = static_cast<uint16_t*>(temp_scoped_alloc_->Alloc( - temp_bit_vector_size_ * sizeof(*temp_insn_data_), kArenaAllocGrowableArray)); + temp_.smi.num_indexes = method_lowering_infos_.size(); + temp_.smi.processed_indexes = new (temp_scoped_alloc_.get()) ArenaBitVector( + temp_scoped_alloc_.get(), temp_.smi.num_indexes, false, kBitMapMisc); + temp_.smi.processed_indexes->ClearAllBits(); + temp_.smi.lowering_infos = static_cast<uint16_t*>(temp_scoped_alloc_->Alloc( + temp_.smi.num_indexes * sizeof(*temp_.smi.lowering_infos), kArenaAllocGrowableArray)); } void MIRGraph::InlineSpecialMethods(BasicBlock* bb) { @@ -1477,10 +1477,12 @@ void MIRGraph::InlineSpecialMethods(BasicBlock* bb) { } void MIRGraph::InlineSpecialMethodsEnd() { - DCHECK(temp_insn_data_ != nullptr); - temp_insn_data_ = nullptr; - DCHECK(temp_bit_vector_ != nullptr); - temp_bit_vector_ = nullptr; + // Clean up temporaries. + DCHECK(temp_.smi.lowering_infos != nullptr); + temp_.smi.lowering_infos = nullptr; + temp_.smi.num_indexes = 0u; + DCHECK(temp_.smi.processed_indexes != nullptr); + temp_.smi.processed_indexes = nullptr; DCHECK(temp_scoped_alloc_.get() != nullptr); temp_scoped_alloc_.reset(); } diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index b4eebb320e..f15d707a5c 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -288,18 +288,12 @@ void ArmMir2Lir::GenMoveException(RegLocation rl_dest) { StoreValue(rl_dest, rl_result); } -/* - * Mark garbage collection card. Skip if the value we're storing is null. - */ -void ArmMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { +void ArmMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { RegStorage reg_card_base = AllocTemp(); RegStorage reg_card_no = AllocTemp(); - LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); LoadWordDisp(rs_rARM_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base); OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); - LIR* target = NewLIR0(kPseudoTargetLabel); - branch_over->target = target; FreeTemp(reg_card_base); FreeTemp(reg_card_no); } @@ -536,8 +530,8 @@ static int ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSE if (direct_code == 0) { // kInvokeTgt := arg0_ref->entrypoint cg->LoadWordDisp(arg0_ref, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt)); + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmPointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt)); } break; default: diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index d2351997b7..e8d0c32ffd 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -106,7 +106,9 @@ class ArmMir2Lir FINAL : public Mir2Lir { OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; - void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg); + + /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) + void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; // Required for target - register utilities. RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 57544b5187..cab039bfd4 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -16,6 +16,7 @@ /* This file contains codegen for the Thumb2 ISA. */ +#include "arch/instruction_set_features.h" #include "arm_lir.h" #include "codegen_arm.h" #include "dex/quick/mir_to_lir-inl.h" @@ -1119,7 +1120,9 @@ LIR* ArmMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* targe } bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP != 0 + if (!cu_->GetInstructionSetFeatures()->IsSmp()) { + return false; + } // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one. LIR* barrier = last_lir_insn_; @@ -1149,9 +1152,6 @@ bool ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { DCHECK(!barrier->flags.use_def_invalid); barrier->u.m.def_mask = &kEncodeAll; return ret; -#else - return false; -#endif } void ArmMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index 973279e8b7..f8a7310c20 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -320,6 +320,7 @@ enum A64Opcode { kA64Mul3rrr, // mul [00011011000] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0]. kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2". + kA64Nop0, // nop alias of "hint #0" [11010101000000110010000000011111]. kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. kA64Orr4rrro, // orr [s0101010] shift[23-22] [0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0]. kA64Ret, // ret [11010110010111110000001111000000]. @@ -332,7 +333,7 @@ enum A64Opcode { kA64Scvtf2fw, // scvtf [000111100s100010000000] rn[9-5] rd[4-0]. kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0]. kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0]. - kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. + kA64Smull3xww, // smull [10011011001] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 9cdabf18f0..cab11cc4a5 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -14,8 +14,10 @@ * limitations under the License. */ -#include "arm64_lir.h" #include "codegen_arm64.h" + +#include "arch/arm64/instruction_set_features_arm64.h" +#include "arm64_lir.h" #include "dex/quick/mir_to_lir-inl.h" namespace art { @@ -468,13 +470,17 @@ const A64EncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "mul", "!0r, !1r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000), - kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, - kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, - "msub", "!0r, !1r, !3r, !2r", kFixupNone), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, + kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP, + "msub", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769), ENCODING_MAP(WIDE(kA64Neg3rro), SF_VARIANTS(0x4b0003e0), kFmtRegR, 4, 0, kFmtRegR, 20, 16, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "neg", "!0r, !1r!2o", kFixupNone), + ENCODING_MAP(kA64Nop0, NO_VARIANTS(0xd503201f), + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND, + "nop", "", kFixupNone), ENCODING_MAP(WIDE(kA64Orr3Rrl), SF_VARIANTS(0x32000000), kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, @@ -523,10 +529,10 @@ const A64EncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "sdiv", "!0r, !1r, !2r", kFixupNone), - ENCODING_MAP(WIDE(kA64Smaddl4xwwx), NO_VARIANTS(0x9b200000), + ENCODING_MAP(kA64Smull3xww, NO_VARIANTS(0x9b207c00), kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16, - kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123, - "smaddl", "!0x, !1w, !2w, !3x", kFixupNone), + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "smull", "!0x, !1w, !2w", kFixupNone), ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00), kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, @@ -988,6 +994,30 @@ void Arm64Mir2Lir::AssembleLIR() { lir->operands[1] = delta; break; } + case kFixupA53Erratum835769: + // Avoid emitting code that could trigger Cortex A53's erratum 835769. + // This fixup should be carried out for all multiply-accumulate instructions: madd, msub, + // smaddl, smsubl, umaddl and umsubl. + if (cu_->GetInstructionSetFeatures()->AsArm64InstructionSetFeatures() + ->NeedFixCortexA53_835769()) { + // Check that this is a 64-bit multiply-accumulate. + if (IS_WIDE(lir->opcode)) { + uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags; + // Check that the instruction preceding the multiply-accumulate is a load or store. + if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { + // insert a NOP between the load/store and the multiply-accumulate. + LIR* new_lir = RawLIR(lir->dalvik_offset, kA64Nop0, 0, 0, 0, 0, 0, NULL); + new_lir->offset = lir->offset; + new_lir->flags.fixup = kFixupNone; + new_lir->flags.size = EncodingMap[kA64Nop0].size; + InsertLIRBefore(lir, new_lir); + lir->offset += new_lir->flags.size; + offset_adjustment += new_lir->flags.size; + res = kRetryAll; + } + } + } + break; default: LOG(FATAL) << "Unexpected case " << lir->flags.fixup; } diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 106996ea44..089e4b6709 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -251,20 +251,14 @@ void Arm64Mir2Lir::GenMoveException(RegLocation rl_dest) { StoreValue(rl_dest, rl_result); } -/* - * Mark garbage collection card. Skip if the value we're storing is null. - */ -void Arm64Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { +void Arm64Mir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { RegStorage reg_card_base = AllocTempWide(); RegStorage reg_card_no = AllocTempWide(); // Needs to be wide as addr is ref=64b - LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); LoadWordDisp(rs_xSELF, Thread::CardTableOffset<8>().Int32Value(), reg_card_base); OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); // TODO(Arm64): generate "strb wB, [xB, wC, uxtw]" rather than "strb wB, [xB, xC]"? StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base), 0, kUnsignedByte); - LIR* target = NewLIR0(kPseudoTargetLabel); - branch_over->target = target; FreeTemp(reg_card_base); FreeTemp(reg_card_no); } @@ -473,8 +467,8 @@ static int Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info, if (direct_code == 0) { // kInvokeTgt := arg0_ref->entrypoint cg->LoadWordDisp(arg0_ref, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), - cg->TargetPtrReg(kInvokeTgt)); + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt)); } break; default: diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h index 5182a89474..5e10f80fa5 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -94,7 +94,10 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; LIR* StoreRefIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale) OVERRIDE; - void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE; + + /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) + void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; + LIR* OpCmpMemImmBranch(ConditionCode cond, RegStorage temp_reg, RegStorage base_reg, int offset, int check_value, LIR* target, LIR** compare) OVERRIDE; diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 8a5a58c949..0e00698388 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -16,6 +16,7 @@ /* This file contains codegen for the Thumb2 ISA. */ +#include "arch/instruction_set_features.h" #include "arm64_lir.h" #include "codegen_arm64.h" #include "dex/quick/mir_to_lir-inl.h" @@ -427,8 +428,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_long_mul = AllocTemp(); - NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(), - r_magic.GetReg(), rl_src.reg.GetReg(), rxzr); + NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); switch (pattern) { case Divide3: OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32); @@ -648,7 +648,7 @@ RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegS } OpRegRegReg(kOpDiv, temp, r_src1, r_src2); NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(), - r_src1.GetReg(), r_src2.GetReg()); + r_src2.GetReg(), r_src1.GetReg()); FreeTemp(temp); } return rl_result; @@ -979,7 +979,9 @@ LIR* Arm64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* tar } bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP != 0 + if (!cu_->GetInstructionSetFeatures()->IsSmp()) { + return false; + } // Start off with using the last LIR as the barrier. If it is not enough, then we will generate one. LIR* barrier = last_lir_insn_; @@ -1015,9 +1017,6 @@ bool Arm64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { DCHECK(!barrier->flags.use_def_invalid); barrier->u.m.def_mask = &kEncodeAll; return ret; -#else - return false; -#endif } void Arm64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 9403516641..80cb535307 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -314,6 +314,15 @@ void Mir2Lir::UpdateLIROffsets() { } } +void Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { + DCHECK(val_reg.Valid()); + DCHECK_EQ(val_reg.Is64Bit(), cu_->target64); + LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, nullptr); + UnconditionallyMarkGCCard(tgt_addr_reg); + LIR* target = NewLIR0(kPseudoTargetLabel); + branch_over->target = target; +} + /* Dump instructions and constant pool contents */ void Mir2Lir::CodegenDump() { LOG(INFO) << "Dumping LIR insns for " diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 98ddc36f63..c00f90b6e2 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -416,8 +416,8 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { // share array alignment with ints (see comment at head of function) size_t component_size = sizeof(int32_t); - // Having a range of 0 is legal - if (info->is_range && (elems > 0)) { + if (elems > 5) { + DCHECK(info->is_range); // Non-range insn can't encode more than 5 elems. /* * Bit of ugliness here. We're going generate a mem copy loop * on the register range, but it is possible that some regs @@ -487,7 +487,11 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { OpRegRegImm(kOpAdd, ref_reg, r_dst, -mirror::Array::DataOffset(component_size).Int32Value()); } - } else if (!info->is_range) { + FreeTemp(r_idx); + FreeTemp(r_dst); + FreeTemp(r_src); + } else { + DCHECK_LE(elems, 5); // Usually but not necessarily non-range. // TUNING: interleave for (int i = 0; i < elems; i++) { RegLocation rl_arg; @@ -507,6 +511,15 @@ void Mir2Lir::GenFilledNewArray(CallInfo* info) { } } } + if (elems != 0 && info->args[0].ref) { + // If there is at least one potentially non-null value, unconditionally mark the GC card. + for (int i = 0; i < elems; i++) { + if (!mir_graph_->IsConstantNullRef(info->args[i])) { + UnconditionallyMarkGCCard(ref_reg); + break; + } + } + } if (info->result.location != kLocInvalid) { StoreValue(info->result, GetReturn(kRefReg)); } diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 4cb12f1dc9..a7900ae7df 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -476,9 +476,10 @@ static void CommonCallCodeLoadClassIntoArg0(const CallInfo* info, Mir2Lir* cg) { static bool CommonCallCodeLoadCodePointerIntoInvokeTgt(const RegStorage* alt_from, const CompilationUnit* cu, Mir2Lir* cg) { if (cu->instruction_set != kX86 && cu->instruction_set != kX86_64) { + int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + InstructionSetPointerSize(cu->instruction_set)).Int32Value(); // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt] - cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(), + cg->LoadWordDisp(alt_from == nullptr ? cg->TargetReg(kArg0, kRef) : *alt_from, offset, cg->TargetPtrReg(kInvokeTgt)); return true; } diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index ed73ef0a00..3bb81bf28e 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -222,19 +222,13 @@ void MipsMir2Lir::GenMoveException(RegLocation rl_dest) { StoreValue(rl_dest, rl_result); } -/* - * Mark garbage collection card. Skip if the value we're storing is null. - */ -void MipsMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { +void MipsMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { RegStorage reg_card_base = AllocTemp(); RegStorage reg_card_no = AllocTemp(); - LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); // NOTE: native pointer. LoadWordDisp(rs_rMIPS_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base); OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); - LIR* target = NewLIR0(kPseudoTargetLabel); - branch_over->target = target; FreeTemp(reg_card_base); FreeTemp(reg_card_no); } diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 7e9d80df65..e08846c325 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -49,7 +49,9 @@ class MipsMir2Lir FINAL : public Mir2Lir { OpSize size) OVERRIDE; LIR* GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest); LIR* GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src); - void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg); + + /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) + void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; // Required for target - register utilities. RegStorage Solo64ToPair64(RegStorage reg); diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 92ef70db7e..70ef991dee 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -201,6 +201,16 @@ void Mir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { RegStorage reg_arg_low = GetArgMappingToPhysicalReg(in_position); RegStorage reg_arg_high = GetArgMappingToPhysicalReg(in_position + 1); + if (cu_->instruction_set == kX86) { + // Can't handle double split between reg & memory. Flush reg half to memory. + if (rl_dest.reg.IsDouble() && (reg_arg_low.Valid() != reg_arg_high.Valid())) { + DCHECK(reg_arg_low.Valid()); + DCHECK(!reg_arg_high.Valid()); + Store32Disp(TargetPtrReg(kSp), offset, reg_arg_low); + reg_arg_low = RegStorage::InvalidReg(); + } + } + if (reg_arg_low.Valid() && reg_arg_high.Valid()) { OpRegCopyWide(rl_dest.reg, RegStorage::MakeRegPair(reg_arg_low, reg_arg_high)); } else if (reg_arg_low.Valid() && !reg_arg_high.Valid()) { @@ -1238,7 +1248,7 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { if (opcode == kMirOpCheck) { // Combine check and work halves of throwing instruction. MIR* work_half = mir->meta.throw_insn; - mir->dalvikInsn.opcode = work_half->dalvikInsn.opcode; + mir->dalvikInsn = work_half->dalvikInsn; mir->optimization_flags = work_half->optimization_flags; mir->meta = work_half->meta; // Whatever the work_half had, we need to copy it. opcode = work_half->dalvikInsn.opcode; diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 13ebc1e5d6..886b238ee3 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -1071,6 +1071,13 @@ class Mir2Lir : public Backend { // Update LIR for verbose listings. void UpdateLIROffsets(); + /** + * @brief Mark a garbage collection card. Skip if the stored value is null. + * @param val_reg the register holding the stored value to check against null. + * @param tgt_addr_reg the address of the object or array where the value was stored. + */ + void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg); + /* * @brief Load the address of the dex method into the register. * @param target_method The MethodReference of the method to be invoked. @@ -1139,7 +1146,12 @@ class Mir2Lir : public Backend { OpSize size, VolatileKind is_volatile) = 0; virtual LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) = 0; - virtual void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) = 0; + + /** + * @brief Unconditionally mark a garbage collection card. + * @param tgt_addr_reg the address of the object or array where the value was stored. + */ + virtual void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) = 0; // Required for target - register utilities. diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index a54c55ffce..8d4cb3c5e9 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -613,7 +613,8 @@ CompiledMethod* QuickCompiler::JniCompile(uint32_t access_flags, } uintptr_t QuickCompiler::GetEntryPointOf(mirror::ArtMethod* method) const { - return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()); + return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize( + InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet()))); } bool QuickCompiler::WriteElf(art::File* file, diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 61dcc28afc..a808459715 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -136,23 +136,16 @@ void X86Mir2Lir::GenMoveException(RegLocation rl_dest) { StoreValue(rl_dest, rl_result); } -/* - * Mark garbage collection card. Skip if the value we're storing is null. - */ -void X86Mir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) { +void X86Mir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { DCHECK_EQ(tgt_addr_reg.Is64Bit(), cu_->target64); - DCHECK_EQ(val_reg.Is64Bit(), cu_->target64); RegStorage reg_card_base = AllocTempRef(); RegStorage reg_card_no = AllocTempRef(); - LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL); int ct_offset = cu_->target64 ? Thread::CardTableOffset<8>().Int32Value() : Thread::CardTableOffset<4>().Int32Value(); NewLIR2(cu_->target64 ? kX86Mov64RT : kX86Mov32RT, reg_card_base.GetReg(), ct_offset); OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); - LIR* target = NewLIR0(kPseudoTargetLabel); - branch_over->target = target; FreeTemp(reg_card_base); FreeTemp(reg_card_no); } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index d57dffb01d..26641f8d59 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -94,7 +94,10 @@ class X86Mir2Lir : public Mir2Lir { OpSize size, VolatileKind is_volatile) OVERRIDE; LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) OVERRIDE; - void MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) OVERRIDE; + + /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) + void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; + void GenImplicitNullCheck(RegStorage reg, int opt_flags) OVERRIDE; // Required for target - register utilities. diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index db2f272436..998aeff368 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -18,6 +18,7 @@ #include <inttypes.h> #include <string> +#include "arch/instruction_set_features.h" #include "backend_x86.h" #include "codegen_x86.h" #include "dex/compiler_internals.h" @@ -594,7 +595,9 @@ bool X86Mir2Lir::ProvidesFullMemoryBarrier(X86OpCode opcode) { } bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP != 0 + if (!cu_->GetInstructionSetFeatures()->IsSmp()) { + return false; + } // Start off with using the last LIR as the barrier. If it is not enough, then we will update it. LIR* mem_barrier = last_lir_insn_; @@ -630,9 +633,6 @@ bool X86Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) { mem_barrier->u.m.def_mask = &kEncodeAll; } return ret; -#else - return false; -#endif } void X86Mir2Lir::CompilerInitializeRegAlloc() { @@ -1006,7 +1006,8 @@ LIR* X86Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info) { call_insn = CallWithLinkerFixup(method_info.GetTargetMethod(), method_info.GetSharpType()); } else { call_insn = OpMem(kOpBlx, TargetReg(kArg0, kRef), - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + cu_->target64 ? 8 : 4).Int32Value()); } } else { call_insn = GenInvokeNoInlineCall(this, method_info.GetSharpType()); @@ -2262,7 +2263,8 @@ void X86Mir2Lir::GenReduceVector(MIR* mir) { StoreFinalValue(rl_dest, rl_result); } else { int displacement = SRegOffset(rl_result.s_reg_low); - LIR *l = NewLIR3(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg()); + LIR *l = NewLIR4(extr_opcode, rs_rX86_SP_32.GetReg(), displacement, vector_src.GetReg(), + extract_index); AnnotateDalvikRegAccess(l, displacement >> 2, true /* is_load */, is_wide /* is_64bit */); AnnotateDalvikRegAccess(l, displacement >> 2, false /* is_load */, is_wide /* is_64bit */); } diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc index c1c79caa19..ad3222cd84 100644 --- a/compiler/dex/quick/x86/utility_x86.cc +++ b/compiler/dex/quick/x86/utility_x86.cc @@ -488,6 +488,7 @@ LIR* X86Mir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, case kOpAdc: case kOpAnd: case kOpXor: + case kOpMul: break; default: LOG(FATAL) << "Bad case in OpRegRegReg " << op; diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc index d3d76badd0..ed3388285f 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -126,7 +126,7 @@ bool MIRGraph::FillDefBlockMatrix(BasicBlock* bb) { for (uint32_t idx : bb->data_flow_info->def_v->Indexes()) { /* Block bb defines register idx */ - temp_bit_matrix_[idx]->SetBit(bb->id); + temp_.ssa.def_block_matrix[idx]->SetBit(bb->id); } return true; } @@ -135,16 +135,16 @@ void MIRGraph::ComputeDefBlockMatrix() { int num_registers = GetNumOfCodeAndTempVRs(); /* Allocate num_registers bit vector pointers */ DCHECK(temp_scoped_alloc_ != nullptr); - DCHECK(temp_bit_matrix_ == nullptr); - temp_bit_matrix_ = static_cast<ArenaBitVector**>( + DCHECK(temp_.ssa.def_block_matrix == nullptr); + temp_.ssa.def_block_matrix = static_cast<ArenaBitVector**>( temp_scoped_alloc_->Alloc(sizeof(ArenaBitVector*) * num_registers, kArenaAllocDFInfo)); int i; /* Initialize num_register vectors with num_blocks bits each */ for (i = 0; i < num_registers; i++) { - temp_bit_matrix_[i] = new (temp_scoped_alloc_.get()) ArenaBitVector(arena_, GetNumBlocks(), - false, kBitMapBMatrix); - temp_bit_matrix_[i]->ClearAllBits(); + temp_.ssa.def_block_matrix[i] = new (temp_scoped_alloc_.get()) ArenaBitVector( + arena_, GetNumBlocks(), false, kBitMapBMatrix); + temp_.ssa.def_block_matrix[i]->ClearAllBits(); } AllNodesIterator iter(this); @@ -163,7 +163,7 @@ void MIRGraph::ComputeDefBlockMatrix() { int num_regs = GetNumOfCodeVRs(); int in_reg = GetFirstInVR(); for (; in_reg < num_regs; in_reg++) { - temp_bit_matrix_[in_reg]->SetBit(GetEntryBlock()->id); + temp_.ssa.def_block_matrix[in_reg]->SetBit(GetEntryBlock()->id); } } @@ -435,32 +435,32 @@ void MIRGraph::ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src * insert a phi node if the variable is live-in to the block. */ bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb) { - DCHECK_EQ(temp_bit_vector_size_, cu_->mir_graph.get()->GetNumOfCodeAndTempVRs()); - ArenaBitVector* temp_dalvik_register_v = temp_bit_vector_; + DCHECK_EQ(temp_.ssa.num_vregs, cu_->mir_graph.get()->GetNumOfCodeAndTempVRs()); + ArenaBitVector* temp_live_vregs = temp_.ssa.work_live_vregs; if (bb->data_flow_info == NULL) { return false; } - temp_dalvik_register_v->Copy(bb->data_flow_info->live_in_v); + temp_live_vregs->Copy(bb->data_flow_info->live_in_v); BasicBlock* bb_taken = GetBasicBlock(bb->taken); BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through); if (bb_taken && bb_taken->data_flow_info) - ComputeSuccLineIn(temp_dalvik_register_v, bb_taken->data_flow_info->live_in_v, + ComputeSuccLineIn(temp_live_vregs, bb_taken->data_flow_info->live_in_v, bb->data_flow_info->def_v); if (bb_fall_through && bb_fall_through->data_flow_info) - ComputeSuccLineIn(temp_dalvik_register_v, bb_fall_through->data_flow_info->live_in_v, + ComputeSuccLineIn(temp_live_vregs, bb_fall_through->data_flow_info->live_in_v, bb->data_flow_info->def_v); if (bb->successor_block_list_type != kNotUsed) { for (SuccessorBlockInfo* successor_block_info : bb->successor_blocks) { BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block); if (succ_bb->data_flow_info) { - ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v, + ComputeSuccLineIn(temp_live_vregs, succ_bb->data_flow_info->live_in_v, bb->data_flow_info->def_v); } } } - if (!temp_dalvik_register_v->Equal(bb->data_flow_info->live_in_v)) { - bb->data_flow_info->live_in_v->Copy(temp_dalvik_register_v); + if (!temp_live_vregs->Equal(bb->data_flow_info->live_in_v)) { + bb->data_flow_info->live_in_v->Copy(temp_live_vregs); return true; } return false; @@ -482,7 +482,7 @@ void MIRGraph::InsertPhiNodes() { /* Iterate through each Dalvik register */ for (dalvik_reg = GetNumOfCodeAndTempVRs() - 1; dalvik_reg >= 0; dalvik_reg--) { - input_blocks->Copy(temp_bit_matrix_[dalvik_reg]); + input_blocks->Copy(temp_.ssa.def_block_matrix[dalvik_reg]); phi_blocks->ClearAllBits(); do { // TUNING: When we repeat this, we could skip indexes from the previous pass. diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 1805d59961..ebf7874dcf 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -240,7 +240,8 @@ inline int CompilerDriver::IsFastInvoke( bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) && (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && resolved_method->GetMethodIndex() < methods_class->GetVTableLength() && - (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method); + (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method) && + !resolved_method->IsAbstract(); if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 7e2be3ee0c..dac1ef4c06 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -105,13 +105,16 @@ TEST_F(ImageTest, WriteRead) { ASSERT_TRUE(success_image); bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin()); ASSERT_TRUE(success_fixup); + + ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file " + << oat_file.GetFilename(); } { std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); ASSERT_TRUE(file.get() != NULL); ImageHeader image_header; - file->ReadFully(&image_header, sizeof(image_header)); + ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); ASSERT_TRUE(image_header.IsValid()); ASSERT_GE(image_header.GetImageBitmapOffset(), sizeof(image_header)); ASSERT_NE(0U, image_header.GetImageBitmapSize()); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index b7283a4e19..4f5026dee3 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -54,7 +54,8 @@ #include "runtime.h" #include "scoped_thread_state_change.h" #include "handle_scope-inl.h" -#include "utils.h" + +#include <numeric> using ::art::mirror::ArtField; using ::art::mirror::ArtMethod; @@ -67,12 +68,15 @@ using ::art::mirror::String; namespace art { +// Separate objects into multiple bins to optimize dirty memory use. +static constexpr bool kBinObjects = true; + bool ImageWriter::PrepareImageAddressSpace() { + target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet()); { Thread::Current()->TransitionFromSuspendedToRunnable(); PruneNonImageClasses(); // Remove junk ComputeLazyFieldsForImageClasses(); // Add useful information - ComputeEagerResolvedStrings(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); } gc::Heap* heap = Runtime::Current()->GetHeap(); @@ -149,6 +153,11 @@ bool ImageWriter::Write(const std::string& image_filename, SetOatChecksumFromElfFile(oat_file.get()); + if (oat_file->FlushCloseOrErase() != 0) { + LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location; + return false; + } + std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str())); ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); if (image_file.get() == NULL) { @@ -157,6 +166,7 @@ bool ImageWriter::Write(const std::string& image_filename, } if (fchmod(image_file->Fd(), 0644) != 0) { PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; + image_file->Erase(); return EXIT_FAILURE; } @@ -164,6 +174,7 @@ bool ImageWriter::Write(const std::string& image_filename, CHECK_EQ(image_end_, image_header->GetImageSize()); if (!image_file->WriteFully(image_->Begin(), image_end_)) { PLOG(ERROR) << "Failed to write image file " << image_filename; + image_file->Erase(); return false; } @@ -173,64 +184,237 @@ bool ImageWriter::Write(const std::string& image_filename, image_header->GetImageBitmapSize(), image_header->GetImageBitmapOffset())) { PLOG(ERROR) << "Failed to write image file " << image_filename; + image_file->Erase(); return false; } + if (image_file->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close image file " << image_filename; + return false; + } return true; } -void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { +void ImageWriter::SetImageOffset(mirror::Object* object, + ImageWriter::BinSlot bin_slot, + size_t offset) { DCHECK(object != nullptr); DCHECK_NE(offset, 0U); - DCHECK(!IsImageOffsetAssigned(object)); mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + offset); DCHECK_ALIGNED(obj, kObjectAlignment); - image_bitmap_->Set(obj); + + image_bitmap_->Set(obj); // Mark the obj as mutated, since we will end up changing it. + { + // Remember the object-inside-of-the-image's hash code so we can restore it after the copy. + auto hash_it = saved_hashes_map_.find(bin_slot); + if (hash_it != saved_hashes_map_.end()) { + std::pair<BinSlot, uint32_t> slot_hash = *hash_it; + saved_hashes_.push_back(std::make_pair(obj, slot_hash.second)); + saved_hashes_map_.erase(hash_it); + } + } + // The object is already deflated from when we set the bin slot. Just overwrite the lock word. + object->SetLockWord(LockWord::FromForwardingAddress(offset), false); + DCHECK(IsImageOffsetAssigned(object)); +} + +void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot bin_slot) { + DCHECK(object != nullptr); + DCHECK_NE(image_objects_offset_begin_, 0u); + + size_t previous_bin_sizes = GetBinSizeSum(bin_slot.GetBin()); // sum sizes in [0..bin#) + size_t new_offset = image_objects_offset_begin_ + previous_bin_sizes + bin_slot.GetIndex(); + DCHECK_ALIGNED(new_offset, kObjectAlignment); + + SetImageOffset(object, bin_slot, new_offset); + DCHECK_LT(new_offset, image_end_); +} + +bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const { + // Will also return true if the bin slot was assigned since we are reusing the lock word. + DCHECK(object != nullptr); + return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress; +} + +size_t ImageWriter::GetImageOffset(mirror::Object* object) const { + DCHECK(object != nullptr); + DCHECK(IsImageOffsetAssigned(object)); + LockWord lock_word = object->GetLockWord(false); + size_t offset = lock_word.ForwardingAddress(); + DCHECK_LT(offset, image_end_); + return offset; +} + +void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) { + DCHECK(object != nullptr); + DCHECK(!IsImageOffsetAssigned(object)); + DCHECK(!IsImageBinSlotAssigned(object)); + // Before we stomp over the lock word, save the hash code for later. Monitor::Deflate(Thread::Current(), object);; LockWord lw(object->GetLockWord(false)); switch (lw.GetState()) { case LockWord::kFatLocked: { - LOG(FATAL) << "Fat locked object " << obj << " found during object copy"; + LOG(FATAL) << "Fat locked object " << object << " found during object copy"; break; } case LockWord::kThinLocked: { - LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; + LOG(FATAL) << "Thin locked object " << object << " found during object copy"; break; } case LockWord::kUnlocked: // No hash, don't need to save it. break; case LockWord::kHashCode: - saved_hashes_.push_back(std::make_pair(obj, lw.GetHashCode())); + saved_hashes_map_[bin_slot] = lw.GetHashCode(); break; default: LOG(FATAL) << "Unreachable."; UNREACHABLE(); } - object->SetLockWord(LockWord::FromForwardingAddress(offset), false); - DCHECK(IsImageOffsetAssigned(object)); + object->SetLockWord(LockWord::FromForwardingAddress(static_cast<uint32_t>(bin_slot)), + false); + DCHECK(IsImageBinSlotAssigned(object)); } -void ImageWriter::AssignImageOffset(mirror::Object* object) { +void ImageWriter::AssignImageBinSlot(mirror::Object* object) { DCHECK(object != nullptr); - SetImageOffset(object, image_end_); - image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment + size_t object_size; + if (object->IsArtMethod()) { + // Methods are sized based on the target pointer size. + object_size = mirror::ArtMethod::InstanceSize(target_ptr_size_); + } else { + object_size = object->SizeOf(); + } + + // The magic happens here. We segregate objects into different bins based + // on how likely they are to get dirty at runtime. + // + // Likely-to-dirty objects get packed together into the same bin so that + // at runtime their page dirtiness ratio (how many dirty objects a page has) is + // maximized. + // + // This means more pages will stay either clean or shared dirty (with zygote) and + // the app will use less of its own (private) memory. + Bin bin = kBinRegular; + + if (kBinObjects) { + // + // Changing the bin of an object is purely a memory-use tuning. + // It has no change on runtime correctness. + // + // Memory analysis has determined that the following types of objects get dirtied + // the most: + // + // * Class'es which are verified [their clinit runs only at runtime] + // - classes in general [because their static fields get overwritten] + // - initialized classes with all-final statics are unlikely to be ever dirty, + // so bin them separately + // * Art Methods that are: + // - native [their native entry point is not looked up until runtime] + // - have declaring classes that aren't initialized + // [their interpreter/quick entry points are trampolines until the class + // becomes initialized] + // + // We also assume the following objects get dirtied either never or extremely rarely: + // * Strings (they are immutable) + // * Art methods that aren't native and have initialized declared classes + // + // We assume that "regular" bin objects are highly unlikely to become dirtied, + // so packing them together will not result in a noticeably tighter dirty-to-clean ratio. + // + if (object->IsClass()) { + bin = kBinClassVerified; + mirror::Class* klass = object->AsClass(); + + if (klass->GetStatus() == Class::kStatusInitialized) { + bin = kBinClassInitialized; + + // If the class's static fields are all final, put it into a separate bin + // since it's very likely it will stay clean. + uint32_t num_static_fields = klass->NumStaticFields(); + if (num_static_fields == 0) { + bin = kBinClassInitializedFinalStatics; + } else { + // Maybe all the statics are final? + bool all_final = true; + for (uint32_t i = 0; i < num_static_fields; ++i) { + ArtField* field = klass->GetStaticField(i); + if (!field->IsFinal()) { + all_final = false; + break; + } + } + + if (all_final) { + bin = kBinClassInitializedFinalStatics; + } + } + } + } else if (object->IsArtMethod<kVerifyNone>()) { + mirror::ArtMethod* art_method = down_cast<ArtMethod*>(object); + if (art_method->IsNative()) { + bin = kBinArtMethodNative; + } else { + mirror::Class* declaring_class = art_method->GetDeclaringClass(); + if (declaring_class->GetStatus() != Class::kStatusInitialized) { + bin = kBinArtMethodNotInitialized; + } else { + // This is highly unlikely to dirty since there's no entry points to mutate. + bin = kBinArtMethodsManagedInitialized; + } + } + } else if (object->GetClass<kVerifyNone>()->IsStringClass()) { + bin = kBinString; // Strings are almost always immutable (except for object header). + } // else bin = kBinRegular + } + + size_t current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). + // Move the current bin size up to accomodate the object we just assigned a bin slot. + size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment + bin_slot_sizes_[bin] += offset_delta; + + BinSlot new_bin_slot(bin, current_offset); + SetImageBinSlot(object, new_bin_slot); + + ++bin_slot_count_[bin]; + + DCHECK_LT(GetBinSizeSum(), image_->Size()); + + // Grow the image closer to the end by the object we just assigned. + image_end_ += offset_delta; DCHECK_LT(image_end_, image_->Size()); } -bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const { +bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { DCHECK(object != nullptr); - return object->GetLockWord(false).GetState() == LockWord::kForwardingAddress; + + // We always stash the bin slot into a lockword, in the 'forwarding address' state. + // If it's in some other state, then we haven't yet assigned an image bin slot. + if (object->GetLockWord(false).GetState() != LockWord::kForwardingAddress) { + return false; + } else if (kIsDebugBuild) { + LockWord lock_word = object->GetLockWord(false); + size_t offset = lock_word.ForwardingAddress(); + BinSlot bin_slot(offset); + DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]) + << "bin slot offset should not exceed the size of that bin"; + } + return true; } -size_t ImageWriter::GetImageOffset(mirror::Object* object) const { +ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const { DCHECK(object != nullptr); - DCHECK(IsImageOffsetAssigned(object)); + DCHECK(IsImageBinSlotAssigned(object)); + LockWord lock_word = object->GetLockWord(false); - size_t offset = lock_word.ForwardingAddress(); - DCHECK_LT(offset, image_end_); - return offset; + size_t offset = lock_word.ForwardingAddress(); // TODO: ForwardingAddress should be uint32_t + DCHECK_LE(offset, std::numeric_limits<uint32_t>::max()); + + BinSlot bin_slot(static_cast<uint32_t>(offset)); + DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]); + + return bin_slot; } bool ImageWriter::AllocMemory() { @@ -265,6 +449,149 @@ bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) { return true; } +// Count the number of strings in the heap and put the result in arg as a size_t pointer. +static void CountStringsCallback(Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (obj->GetClass()->IsStringClass()) { + ++*reinterpret_cast<size_t*>(arg); + } +} + +// Collect all the java.lang.String in the heap and put them in the output strings_ array. +class StringCollector { + public: + StringCollector(Handle<mirror::ObjectArray<mirror::String>> strings, size_t index) + : strings_(strings), index_(index) { + } + static void Callback(Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + auto* collector = reinterpret_cast<StringCollector*>(arg); + if (obj->GetClass()->IsStringClass()) { + collector->strings_->SetWithoutChecks<false>(collector->index_++, obj->AsString()); + } + } + size_t GetIndex() const { + return index_; + } + + private: + Handle<mirror::ObjectArray<mirror::String>> strings_; + size_t index_; +}; + +// Compare strings based on length, used for sorting strings by length / reverse length. +class StringLengthComparator { + public: + explicit StringLengthComparator(Handle<mirror::ObjectArray<mirror::String>> strings) + : strings_(strings) { + } + bool operator()(size_t a, size_t b) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return strings_->GetWithoutChecks(a)->GetLength() < strings_->GetWithoutChecks(b)->GetLength(); + } + + private: + Handle<mirror::ObjectArray<mirror::String>> strings_; +}; + +// Normal string < comparison through the chars_ array. +class SubstringComparator { + public: + explicit SubstringComparator(const std::vector<uint16_t>* const chars) : chars_(chars) { + } + bool operator()(const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b) { + return std::lexicographical_compare(chars_->begin() + a.first, + chars_->begin() + a.first + a.second, + chars_->begin() + b.first, + chars_->begin() + b.first + b.second); + } + + private: + const std::vector<uint16_t>* const chars_; +}; + +void ImageWriter::ProcessStrings() { + size_t total_strings = 0; + gc::Heap* heap = Runtime::Current()->GetHeap(); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + { + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + heap->VisitObjects(CountStringsCallback, &total_strings); // Count the strings. + } + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + auto strings = hs.NewHandle(cl->AllocStringArray(self, total_strings)); + StringCollector string_collector(strings, 0U); + { + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + // Read strings into the array. + heap->VisitObjects(StringCollector::Callback, &string_collector); + } + // Some strings could have gotten freed if AllocStringArray caused a GC. + CHECK_LE(string_collector.GetIndex(), total_strings); + total_strings = string_collector.GetIndex(); + size_t total_length = 0; + std::vector<size_t> reverse_sorted_strings; + for (size_t i = 0; i < total_strings; ++i) { + mirror::String* s = strings->GetWithoutChecks(i); + // Look up the string in the array. + total_length += s->GetLength(); + reverse_sorted_strings.push_back(i); + } + // Sort by reverse length. + StringLengthComparator comparator(strings); + std::sort(reverse_sorted_strings.rbegin(), reverse_sorted_strings.rend(), comparator); + // Deduplicate prefixes and add strings to the char array. + std::vector<uint16_t> combined_chars(total_length, 0U); + size_t num_chars = 0; + // Characters of strings which are non equal prefix of another string (not the same string). + // We don't count the savings from equal strings since these would get interned later anyways. + size_t prefix_saved_chars = 0; + std::set<std::pair<size_t, size_t>, SubstringComparator> existing_strings(( + SubstringComparator(&combined_chars))); + for (size_t i = 0; i < total_strings; ++i) { + mirror::String* s = strings->GetWithoutChecks(reverse_sorted_strings[i]); + // Add the string to the end of the char array. + size_t length = s->GetLength(); + for (size_t j = 0; j < length; ++j) { + combined_chars[num_chars++] = s->CharAt(j); + } + // Try to see if the string exists as a prefix of an existing string. + size_t new_offset = 0; + std::pair<size_t, size_t> new_string(num_chars - length, length); + auto it = existing_strings.lower_bound(new_string); + bool is_prefix = false; + if (it != existing_strings.end()) { + CHECK_LE(length, it->second); + is_prefix = std::equal(combined_chars.begin() + it->first, + combined_chars.begin() + it->first + it->second, + combined_chars.begin() + new_string.first); + } + if (is_prefix) { + // Shares a prefix, set the offset to where the new offset will be. + new_offset = it->first; + // Remove the added chars. + num_chars -= length; + if (it->second != length) { + prefix_saved_chars += length; + } + } else { + new_offset = new_string.first; + existing_strings.insert(new_string); + } + s->SetOffset(new_offset); + } + // Allocate and update the char arrays. + auto* array = mirror::CharArray::Alloc(self, num_chars); + for (size_t i = 0; i < num_chars; ++i) { + array->SetWithoutChecks<false>(i, combined_chars[i]); + } + for (size_t i = 0; i < total_strings; ++i) { + strings->GetWithoutChecks(i)->SetArray(array); + } + LOG(INFO) << "Total # image strings=" << total_strings << " combined length=" + << total_length << " prefix saved chars=" << prefix_saved_chars; + ComputeEagerResolvedStrings(); +} + void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) { if (!obj->GetClass()->IsStringClass()) { return; @@ -293,7 +620,7 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATT } } -void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +void ImageWriter::ComputeEagerResolvedStrings() { ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this); } @@ -364,8 +691,7 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { return true; } -void ImageWriter::CheckNonImageClassesRemoved() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +void ImageWriter::CheckNonImageClassesRemoved() { if (compiler_driver_.GetImageClasses() != nullptr) { gc::Heap* heap = Runtime::Current()->GetHeap(); ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); @@ -394,29 +720,29 @@ void ImageWriter::DumpImageClasses() { } } -void ImageWriter::CalculateObjectOffsets(Object* obj) { +void ImageWriter::CalculateObjectBinSlots(Object* obj) { DCHECK(obj != NULL); // if it is a string, we want to intern it if its not interned. if (obj->GetClass()->IsStringClass()) { // we must be an interned string that was forward referenced and already assigned - if (IsImageOffsetAssigned(obj)) { + if (IsImageBinSlotAssigned(obj)) { DCHECK_EQ(obj, obj->AsString()->Intern()); return; } mirror::String* const interned = obj->AsString()->Intern(); if (obj != interned) { - if (!IsImageOffsetAssigned(interned)) { + if (!IsImageBinSlotAssigned(interned)) { // interned obj is after us, allocate its location early - AssignImageOffset(interned); + AssignImageBinSlot(interned); } // point those looking for this object to the interned version. - SetImageOffset(obj, GetImageOffset(interned)); + SetImageBinSlot(obj, GetImageBinSlot(interned)); return; } // else (obj == interned), nothing to do but fall through to the normal case } - AssignImageOffset(obj); + AssignImageBinSlot(obj); } ObjectArray<Object>* ImageWriter::CreateImageRoots() const { @@ -497,13 +823,15 @@ void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) // For an unvisited object, visit it then all its children found via fields. void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { - if (!IsImageOffsetAssigned(obj)) { + // Use our own visitor routine (instead of GC visitor) to get better locality between + // an object and its fields + if (!IsImageBinSlotAssigned(obj)) { // Walk instance fields of all objects StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Object> h_obj(hs.NewHandle(obj)); Handle<mirror::Class> klass(hs.NewHandle(obj->GetClass())); // visit the object itself. - CalculateObjectOffsets(h_obj.Get()); + CalculateObjectBinSlots(h_obj.Get()); WalkInstanceFields(h_obj.Get(), klass.Get()); // Walk static fields of a Class. if (h_obj->IsClass()) { @@ -537,6 +865,24 @@ void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) { writer->WalkFieldsInOrder(obj); } +void ImageWriter::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) { + ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); + DCHECK(writer != nullptr); + writer->UnbinObjectsIntoOffset(obj); +} + +void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) { + CHECK(obj != nullptr); + + // We know the bin slot, and the total bin sizes for all objects by now, + // so calculate the object's final image offset. + + DCHECK(IsImageBinSlotAssigned(obj)); + BinSlot bin_slot = GetImageBinSlot(obj); + // Change the lockword from a bin slot into an offset + AssignImageOffset(obj, bin_slot); +} + void ImageWriter::CalculateNewObjectOffsets() { Thread* self = Thread::Current(); StackHandleScope<1> hs(self); @@ -547,16 +893,22 @@ void ImageWriter::CalculateNewObjectOffsets() { // Leave space for the header, but do not write it yet, we need to // know where image_roots is going to end up - image_end_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment + image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); // TODO: Image spaces only? DCHECK_LT(image_end_, image_->Size()); - // Clear any pre-existing monitors which may have been in the monitor words. + image_objects_offset_begin_ = image_end_; + // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots. heap->VisitObjects(WalkFieldsCallback, this); + // Transform each object's bin slot into an offset which will be used to do the final copy. + heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this); + DCHECK(saved_hashes_map_.empty()); // All binslot hashes should've been put into vector by now. } + DCHECK_GT(image_end_, GetBinSizeSum()); + image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots.Get())); // Note that image_end_ is left at end of used space @@ -566,6 +918,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { CHECK_NE(0U, oat_loaded_size); const uint8_t* oat_file_begin = GetOatFileBegin(); const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size; + oat_data_begin_ = oat_file_begin + oat_data_offset; const uint8_t* oat_data_end = oat_data_begin_ + oat_file_->Size(); @@ -587,9 +940,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { compile_pic_); } - -void ImageWriter::CopyAndFixupObjects() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +void ImageWriter::CopyAndFixupObjects() { ScopedAssertNoThreadSuspension ants(Thread::Current(), "ImageWriter"); gc::Heap* heap = Runtime::Current()->GetHeap(); // TODO: heap validation can't handle this fix up pass @@ -612,7 +963,14 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { size_t offset = image_writer->GetImageOffset(obj); uint8_t* dst = image_writer->image_->Begin() + offset; const uint8_t* src = reinterpret_cast<const uint8_t*>(obj); - size_t n = obj->SizeOf(); + size_t n; + if (obj->IsArtMethod()) { + // Size without pointer fields since we don't want to overrun the buffer if target art method + // is 32 bits but source is 64 bits. + n = mirror::ArtMethod::SizeWithoutPointerFields(); + } else { + n = obj->SizeOf(); + } DCHECK_LT(offset + n, image_writer->image_->Size()); memcpy(dst, src, n); Object* copy = reinterpret_cast<Object*>(dst); @@ -622,6 +980,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { image_writer->FixupObject(obj, copy); } +// Rewrite all the references in the copied object to point to their image address equivalent class FixupVisitor { public: FixupVisitor(ImageWriter* image_writer, Object* copy) : image_writer_(image_writer), copy_(copy) { @@ -657,8 +1016,9 @@ class FixupClassVisitor FINAL : public FixupVisitor { void operator()(Object* obj, MemberOffset offset, bool /*is_static*/) const EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { DCHECK(obj->IsClass()); - FixupVisitor::operator()(obj, offset, false); + FixupVisitor::operator()(obj, offset, /*is_static*/false); + // TODO: Remove dead code if (offset.Uint32Value() < mirror::Class::EmbeddedVTableOffset().Uint32Value()) { return; } @@ -692,6 +1052,10 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { } if (orig->IsArtMethod<kVerifyNone>()) { FixupMethod(orig->AsArtMethod<kVerifyNone>(), down_cast<ArtMethod*>(copy)); + } else if (orig->IsClass() && orig->AsClass()->IsArtMethodClass()) { + // Set the right size for the target. + size_t size = mirror::ArtMethod::InstanceSize(target_ptr_size_); + down_cast<mirror::Class*>(copy)->SetObjectSizeWithoutChecks(size); } } @@ -750,29 +1114,48 @@ const uint8_t* ImageWriter::GetQuickEntryPoint(mirror::ArtMethod* method) { void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to // oat_begin_ + // For 64 bit targets we need to repack the current runtime pointer sized fields to the right + // locations. + // Copy all of the fields from the runtime methods to the target methods first since we did a + // bytewise copy earlier. + copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>( + orig->GetEntryPointFromPortableCompiledCode(), target_ptr_size_); + copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>(orig->GetEntryPointFromInterpreter(), + target_ptr_size_); + copy->SetEntryPointFromJniPtrSize<kVerifyNone>(orig->GetEntryPointFromJni(), target_ptr_size_); + copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>( + orig->GetEntryPointFromQuickCompiledCode(), target_ptr_size_); + copy->SetNativeGcMapPtrSize<kVerifyNone>(orig->GetNativeGcMap(), target_ptr_size_); // The resolution method has a special trampoline to call. Runtime* runtime = Runtime::Current(); if (UNLIKELY(orig == runtime->GetResolutionMethod())) { - copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_resolution_trampoline_offset_)); - copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_resolution_trampoline_offset_)); + copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>( + GetOatAddress(portable_resolution_trampoline_offset_), target_ptr_size_); + copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>( + GetOatAddress(quick_resolution_trampoline_offset_), target_ptr_size_); } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() || orig == runtime->GetImtUnimplementedMethod())) { - copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_imt_conflict_trampoline_offset_)); - copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_imt_conflict_trampoline_offset_)); + copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>( + GetOatAddress(portable_imt_conflict_trampoline_offset_), target_ptr_size_); + copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>( + GetOatAddress(quick_imt_conflict_trampoline_offset_), target_ptr_size_); } else { // We assume all methods have code. If they don't currently then we set them to the use the // resolution trampoline. Abstract methods never have code and so we need to make sure their // use results in an AbstractMethodError. We use the interpreter to achieve this. if (UNLIKELY(orig->IsAbstract())) { - copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(GetOatAddress(portable_to_interpreter_bridge_offset_)); - copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(GetOatAddress(quick_to_interpreter_bridge_offset_)); - copy->SetEntryPointFromInterpreter<kVerifyNone>(reinterpret_cast<EntryPointFromInterpreter*> - (const_cast<uint8_t*>(GetOatAddress(interpreter_to_interpreter_bridge_offset_)))); + copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>( + GetOatAddress(portable_to_interpreter_bridge_offset_), target_ptr_size_); + copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>( + GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_); + copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>( + reinterpret_cast<EntryPointFromInterpreter*>(const_cast<uint8_t*>( + GetOatAddress(interpreter_to_interpreter_bridge_offset_))), target_ptr_size_); } else { bool quick_is_interpreted; const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted); - copy->SetEntryPointFromQuickCompiledCode<kVerifyNone>(quick_code); + copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(quick_code, target_ptr_size_); // Portable entrypoint: const uint8_t* portable_code = GetOatAddress(orig->GetPortableOatCodeOffset()); @@ -795,18 +1178,19 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { // initialization. portable_code = GetOatAddress(portable_resolution_trampoline_offset_); } - copy->SetEntryPointFromPortableCompiledCode<kVerifyNone>(portable_code); - + copy->SetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>( + portable_code, target_ptr_size_); // JNI entrypoint: if (orig->IsNative()) { // The native method's pointer is set to a stub to lookup via dlsym. // Note this is not the code_ pointer, that is handled above. - copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_)); + copy->SetEntryPointFromJniPtrSize<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_), + target_ptr_size_); } else { // Normal (non-abstract non-native) methods have various tables to relocate. uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset(); const uint8_t* native_gc_map = GetOatAddress(native_gc_map_offset); - copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map)); + copy->SetNativeGcMapPtrSize<kVerifyNone>(native_gc_map, target_ptr_size_); } // Interpreter entrypoint: @@ -814,9 +1198,11 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { uint32_t interpreter_code = (quick_is_interpreted && portable_is_interpreted) ? interpreter_to_interpreter_bridge_offset_ : interpreter_to_compiled_code_bridge_offset_; - copy->SetEntryPointFromInterpreter<kVerifyNone>( + EntryPointFromInterpreter* interpreter_entrypoint = reinterpret_cast<EntryPointFromInterpreter*>( - const_cast<uint8_t*>(GetOatAddress(interpreter_code)))); + const_cast<uint8_t*>(GetOatAddress(interpreter_code))); + copy->SetEntryPointFromInterpreterPtrSize<kVerifyNone>( + interpreter_entrypoint, target_ptr_size_); } } } @@ -846,4 +1232,32 @@ void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) { image_header->SetOatChecksum(oat_header->GetChecksum()); } +size_t ImageWriter::GetBinSizeSum(ImageWriter::Bin up_to) const { + DCHECK_LE(up_to, kBinSize); + return std::accumulate(&bin_slot_sizes_[0], &bin_slot_sizes_[up_to], /*init*/0); +} + +ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { + // These values may need to get updated if more bins are added to the enum Bin + static_assert(kBinBits == 3, "wrong number of bin bits"); + static_assert(kBinShift == 29, "wrong number of shift"); + static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes"); + + DCHECK_LT(GetBin(), kBinSize); + DCHECK_ALIGNED(GetIndex(), kObjectAlignment); +} + +ImageWriter::BinSlot::BinSlot(Bin bin, uint32_t index) + : BinSlot(index | (static_cast<uint32_t>(bin) << kBinShift)) { + DCHECK_EQ(index, GetIndex()); +} + +ImageWriter::Bin ImageWriter::BinSlot::GetBin() const { + return static_cast<Bin>((lockword_ & kBinMask) >> kBinShift); +} + +uint32_t ImageWriter::BinSlot::GetIndex() const { + return lockword_ & ~kBinMask; +} + } // namespace art diff --git a/compiler/image_writer.h b/compiler/image_writer.h index b0cf2b20ad..8c84b686fa 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -23,15 +23,18 @@ #include <memory> #include <set> #include <string> +#include <ostream> #include "base/macros.h" #include "driver/compiler_driver.h" +#include "gc/space/space.h" #include "mem_map.h" #include "oat_file.h" #include "mirror/dex_cache.h" #include "os.h" #include "safe_map.h" #include "gc/space/space.h" +#include "utils.h" namespace art { @@ -41,13 +44,15 @@ class ImageWriter FINAL { ImageWriter(const CompilerDriver& compiler_driver, uintptr_t image_begin, bool compile_pic) : compiler_driver_(compiler_driver), image_begin_(reinterpret_cast<uint8_t*>(image_begin)), - image_end_(0), image_roots_address_(0), oat_file_(nullptr), + image_end_(0), image_objects_offset_begin_(0), image_roots_address_(0), oat_file_(nullptr), oat_data_begin_(nullptr), interpreter_to_interpreter_bridge_offset_(0), interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0), portable_imt_conflict_trampoline_offset_(0), portable_resolution_trampoline_offset_(0), portable_to_interpreter_bridge_offset_(0), quick_generic_jni_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0), - quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic) { + quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic), + target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), + bin_slot_sizes_(), bin_slot_count_() { CHECK_NE(image_begin, 0U); } @@ -86,14 +91,71 @@ class ImageWriter FINAL { // Mark the objects defined in this space in the given live bitmap. void RecordImageAllocations() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Classify different kinds of bins that objects end up getting packed into during image writing. + enum Bin { + // Likely-clean: + kBinString, // [String] Almost always immutable (except for obj header). + kBinArtMethodsManagedInitialized, // [ArtMethod] Not-native, and initialized. Unlikely to dirty + // Unknown mix of clean/dirty: + kBinRegular, + // Likely-dirty: + // All classes get their own bins since their fields often dirty + kBinClassInitializedFinalStatics, // Class initializers have been run, no non-final statics + kBinClassInitialized, // Class initializers have been run + kBinClassVerified, // Class verified, but initializers haven't been run + kBinArtMethodNative, // Art method that is actually native + kBinArtMethodNotInitialized, // Art method with a declaring class that wasn't initialized + // Don't care about other art methods since they don't dirty + // Add more bins here if we add more segregation code. + kBinSize, + }; + + friend std::ostream& operator<<(std::ostream& stream, const Bin& bin); + + static constexpr size_t kBinBits = MinimumBitsToStore(kBinSize - 1); + // uint32 = typeof(lockword_) + static constexpr size_t kBinShift = BitSizeOf<uint32_t>() - kBinBits; + // 111000.....0 + static constexpr size_t kBinMask = ((static_cast<size_t>(1) << kBinBits) - 1) << kBinShift; + + // We use the lock word to store the bin # and bin index of the object in the image. + // + // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up + // stored in the lock word bit-for-bit when object forwarding addresses are being calculated. + struct BinSlot { + explicit BinSlot(uint32_t lockword); + BinSlot(Bin bin, uint32_t index); + + // The bin an object belongs to, i.e. regular, class/verified, class/initialized, etc. + Bin GetBin() const; + // The offset in bytes from the beginning of the bin. Aligned to object size. + uint32_t GetIndex() const; + // Pack into a single uint32_t, for storing into a lock word. + explicit operator uint32_t() const { return lockword_; } + // Comparison operator for map support + bool operator<(const BinSlot& other) const { return lockword_ < other.lockword_; } + + private: + // Must be the same size as LockWord, any larger and we would truncate the data. + const uint32_t lockword_; + }; + // We use the lock word to store the offset of the object in the image. - void AssignImageOffset(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SetImageOffset(mirror::Object* object, size_t offset) + void AssignImageOffset(mirror::Object* object, BinSlot bin_slot) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetImageOffset(mirror::Object* object, BinSlot bin_slot, size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsImageOffsetAssigned(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void AssignImageBinSlot(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsImageBinSlotAssigned(mirror::Object* object) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void* GetImageAddressCallback(void* writer, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj); @@ -136,13 +198,16 @@ class ImageWriter FINAL { static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Combine string char arrays. + void ProcessStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Remove unwanted classes from various roots. void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static bool NonImageClassesVisitor(mirror::Class* c, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Verify unwanted classes removed. - void CheckNonImageClassesRemoved(); + void CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void CheckNonImageClassesRemovedCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -153,7 +218,9 @@ class ImageWriter FINAL { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ObjectArray<mirror::Object>* CreateImageRoots() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void CalculateObjectOffsets(mirror::Object* obj) + void CalculateObjectBinSlots(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void UnbinObjectsIntoOffset(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) @@ -162,9 +229,11 @@ class ImageWriter FINAL { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void WalkFieldsCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Creates the contiguous image in memory and adjusts pointers. - void CopyAndFixupObjects(); + void CopyAndFixupObjects() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void FixupMethod(mirror::ArtMethod* orig, mirror::ArtMethod* copy) @@ -182,6 +251,9 @@ class ImageWriter FINAL { // Patches references in OatFile to expect runtime addresses. void SetOatChecksumFromElfFile(File* elf_file); + // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. + size_t GetBinSizeSum(Bin up_to = kBinSize) const; + const CompilerDriver& compiler_driver_; // Beginning target image address for the output image. @@ -190,6 +262,9 @@ class ImageWriter FINAL { // Offset to the free space in image_. size_t image_end_; + // Offset from image_begin_ to where the first object is in image_. + size_t image_objects_offset_begin_; + // The image roots address in the image. uint32_t image_roots_address_; @@ -202,6 +277,9 @@ class ImageWriter FINAL { // Saved hashes (objects are inside of the image so that they don't move). std::vector<std::pair<mirror::Object*, uint32_t>> saved_hashes_; + // Saved hashes (objects are bin slots to inside of the image, not yet allocated an address). + std::map<BinSlot, uint32_t> saved_hashes_map_; + // Beginning target oat address for the pointers from the output image to its oat file. const uint8_t* oat_data_begin_; @@ -221,6 +299,13 @@ class ImageWriter FINAL { uint32_t quick_to_interpreter_bridge_offset_; const bool compile_pic_; + // Size of pointers on the target architecture. + size_t target_ptr_size_; + + // Bin slot tracking for dirty object packing + size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin + size_t bin_slot_count_[kBinSize]; // Number of objects in a bin + friend class FixupVisitor; friend class FixupClassVisitor; DISALLOW_COPY_AND_ASSIGN(ImageWriter); diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 3c3aa02502..c3fe75b3f1 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -307,7 +307,9 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, } // 9. Plant call to native code associated with method. - __ Call(main_jni_conv->MethodStackOffset(), mirror::ArtMethod::NativeMethodOffset(), + MemberOffset jni_entrypoint_offset = mirror::ArtMethod::EntryPointFromJniOffset( + InstructionSetPointerSize(instruction_set)); + __ Call(main_jni_conv->MethodStackOffset(), jni_entrypoint_offset, mr_conv->InterproceduralScratchRegister()); // 10. Fix differences in result widths. diff --git a/compiler/llvm/ir_builder.h b/compiler/llvm/ir_builder.h index 03498efcbb..990ba02d3d 100644 --- a/compiler/llvm/ir_builder.h +++ b/compiler/llvm/ir_builder.h @@ -101,10 +101,8 @@ class IRBuilder : public LLVMIRBuilder { // Extend memory barrier //-------------------------------------------------------------------------- void CreateMemoryBarrier(MemBarrierKind barrier_kind) { -#if ANDROID_SMP // TODO: select atomic ordering according to given barrier kind. CreateFence(::llvm::SequentiallyConsistent); -#endif } //-------------------------------------------------------------------------- diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 659c3328fc..c6beb36178 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -309,7 +309,7 @@ class OatWriter::Thumb2RelativeCallPatcher FINAL : public ArmBaseRelativeCallPat arm::Thumb2Assembler assembler; assembler.LoadFromOffset( arm::kLoadWord, arm::PC, arm::R0, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value()); assembler.bkpt(0); std::vector<uint8_t> thunk_code(assembler.CodeSize()); MemoryRegion code(thunk_code.data(), thunk_code.size()); @@ -363,7 +363,8 @@ class OatWriter::Arm64RelativeCallPatcher FINAL : public ArmBaseRelativeCallPatc // The thunk just uses the entry point in the ArtMethod. This works even for calls // to the generic JNI and interpreter trampolines. arm64::Arm64Assembler assembler; - Offset offset(mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); + Offset offset(mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value()); assembler.JumpTo(ManagedRegister(arm64::X0), offset, ManagedRegister(arm64::IP0)); std::vector<uint8_t> thunk_code(assembler.CodeSize()); MemoryRegion code(thunk_code.data(), thunk_code.size()); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index b51b6e7d25..be8631ad42 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -568,12 +568,13 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, return true; } -void HGraphBuilder::BuildCheckedDiv(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) { +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); @@ -597,7 +598,11 @@ void HGraphBuilder::BuildCheckedDiv(uint16_t out_vreg, temps.Add(current_block_->GetLastInstruction()); } - current_block_->AddInstruction(new (arena_) HDiv(type, first, second, dex_pc)); + 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()); } @@ -997,6 +1002,16 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::INT_TO_FLOAT: { + Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimFloat); + break; + } + + case Instruction::INT_TO_DOUBLE: { + Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimDouble); + break; + } + case Instruction::LONG_TO_INT: { Conversion_12x(instruction, Primitive::kPrimLong, Primitive::kPrimInt); break; @@ -1007,6 +1022,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::INT_TO_SHORT: { + Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimShort); + break; + } + case Instruction::INT_TO_CHAR: { Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimChar); break; @@ -1078,14 +1098,14 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::DIV_INT: { - BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(), - dex_pc, Primitive::kPrimInt, false); + BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(), + dex_pc, Primitive::kPrimInt, false, true); break; } case Instruction::DIV_LONG: { - BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(), - dex_pc, Primitive::kPrimLong, false); + BuildCheckedDivRem(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(), + dex_pc, Primitive::kPrimLong, false, true); break; } @@ -1099,6 +1119,18 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 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::AND_INT: { Binop_23x<HAnd>(instruction, Primitive::kPrimInt); break; @@ -1185,14 +1217,26 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::DIV_INT_2ADDR: { - BuildCheckedDiv(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(), - dex_pc, Primitive::kPrimInt, false); + BuildCheckedDivRem(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(), + dex_pc, Primitive::kPrimInt, false, true); break; } case Instruction::DIV_LONG_2ADDR: { - BuildCheckedDiv(instruction.VRegA(), instruction.VRegA(), instruction.VRegB(), - dex_pc, Primitive::kPrimLong, false); + 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; } @@ -1298,8 +1342,15 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::DIV_INT_LIT16: case Instruction::DIV_INT_LIT8: { - BuildCheckedDiv(instruction.VRegA(), instruction.VRegB(), instruction.VRegC(), - dex_pc, Primitive::kPrimInt, true); + 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; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 799e628a78..897bcece7b 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -123,12 +123,13 @@ class HGraphBuilder : public ValueObject { Primitive::Type input_type, Primitive::Type result_type); - void BuildCheckedDiv(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); + 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); diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 4d71cb780a..0b593275c7 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -589,12 +589,14 @@ void CodeGenerator::SaveLiveRegisters(LocationSummary* locations) { if (locations->RegisterContainsObject(i)) { locations->SetStackBit(stack_offset / kVRegSize); } + DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize()); stack_offset += SaveCoreRegister(stack_offset, i); } } for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) { if (register_set->ContainsFloatingPointRegister(i)) { + DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize()); stack_offset += SaveFloatingPointRegister(stack_offset, i); } } @@ -605,12 +607,14 @@ void CodeGenerator::RestoreLiveRegisters(LocationSummary* locations) { size_t stack_offset = first_register_slot_in_slow_path_; for (size_t i = 0, e = GetNumberOfCoreRegisters(); i < e; ++i) { if (register_set->ContainsCoreRegister(i)) { + DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize()); stack_offset += RestoreCoreRegister(stack_offset, i); } } for (size_t i = 0, e = GetNumberOfFloatingPointRegisters(); i < e; ++i) { if (register_set->ContainsFloatingPointRegister(i)) { + DCHECK_LT(stack_offset, GetFrameSize() - FrameEntrySpillSize()); stack_offset += RestoreFloatingPointRegister(stack_offset, i); } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 63bf96ca5a..f906eb8c05 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -168,6 +168,15 @@ class CodeGenerator : public ArenaObject<kArenaAllocMisc> { void EmitParallelMoves(Location from1, Location to1, Location from2, Location to2); + static bool StoreNeedsWriteBarrier(Primitive::Type type, HInstruction* value) { + if (kIsDebugBuild) { + if (type == Primitive::kPrimNot && value->IsIntConstant()) { + CHECK_EQ(value->AsIntConstant()->GetValue(), 0); + } + } + return type == Primitive::kPrimNot && !value->IsIntConstant(); + } + protected: CodeGenerator(HGraph* graph, size_t number_of_core_registers, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 09e1b97570..1701ef5f0a 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -80,7 +80,7 @@ class NullCheckSlowPathARM : public SlowPathCodeARM { public: explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); arm_codegen->InvokeRuntime( @@ -96,7 +96,7 @@ class DivZeroCheckSlowPathARM : public SlowPathCodeARM { public: explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); arm_codegen->InvokeRuntime( @@ -112,7 +112,7 @@ class StackOverflowCheckSlowPathARM : public SlowPathCodeARM { public: StackOverflowCheckSlowPathARM() {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); __ LoadFromOffset(kLoadWord, PC, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value()); @@ -124,10 +124,10 @@ class StackOverflowCheckSlowPathARM : public SlowPathCodeARM { class SuspendCheckSlowPathARM : public SlowPathCodeARM { public: - explicit SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) + SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); codegen->SaveLiveRegisters(instruction_->GetLocations()); @@ -166,7 +166,7 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { index_location_(index_location), length_location_(length_location) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); // We're moving two locations to locations that could overlap, so we need a parallel @@ -199,7 +199,7 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = at_->GetLocations(); CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -245,7 +245,7 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { public: explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); @@ -281,7 +281,7 @@ class TypeCheckSlowPathARM : public SlowPathCodeARM { object_class_(object_class), dex_pc_(dex_pc) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); DCHECK(instruction_->IsCheckCast() || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); @@ -1188,7 +1188,8 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())); // LR = temp[offset_of_quick_compiled_code] __ LoadFromOffset(kLoadWord, LR, temp, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()); + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmWordSize).Int32Value()); // LR() __ blx(LR); @@ -1229,7 +1230,8 @@ void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { __ LoadFromOffset(kLoadWord, temp, receiver.As<Register>(), class_offset); } // temp = temp->GetMethodAt(method_offset); - uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(); + uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmWordSize).Int32Value(); __ LoadFromOffset(kLoadWord, temp, temp, method_offset); // LR = temp->GetEntryPoint(); __ LoadFromOffset(kLoadWord, LR, temp, entry_point); @@ -1265,7 +1267,8 @@ void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) __ LoadFromOffset(kLoadWord, temp, receiver.As<Register>(), class_offset); } // temp = temp->GetImtEntryAt(method_offset); - uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(); + uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmWordSize).Int32Value(); __ LoadFromOffset(kLoadWord, temp, temp, method_offset); // LR = temp->GetEntryPoint(); __ LoadFromOffset(kLoadWord, LR, temp, entry_point); @@ -1367,6 +1370,22 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { } break; + case Primitive::kPrimShort: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-short' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + case Primitive::kPrimInt: switch (input_type) { case Primitive::kPrimLong: @@ -1428,9 +1447,49 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimFloat: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-float' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; + break; + case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-double' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; break; default: @@ -1461,6 +1520,21 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio } break; + case Primitive::kPrimShort: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-short' instruction. + __ sbfx(out.As<Register>(), in.As<Register>(), 0, 16); + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + case Primitive::kPrimInt: switch (input_type) { case Primitive::kPrimLong: @@ -1535,9 +1609,52 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio break; case Primitive::kPrimFloat: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: { + // Processing a Dex `int-to-float' instruction. + __ vmovsr(out.As<SRegister>(), in.As<Register>()); + __ vcvtsi(out.As<SRegister>(), out.As<SRegister>()); + break; + } + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; + break; + case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: { + // Processing a Dex `int-to-double' instruction. + __ vmovsr(out.AsFpuRegisterPairLow<SRegister>(), in.As<Register>()); + __ vcvtdi(FromLowSToD(out.AsFpuRegisterPairLow<SRegister>()), + out.AsFpuRegisterPairLow<SRegister>()); + break; + } + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; break; default: @@ -1842,6 +1959,86 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { } } +void LocationsBuilderARM::VisitRem(HRem* rem) { + LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong + ? LocationSummary::kCall + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); + + switch (rem->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + break; + } + case Primitive::kPrimLong: { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterPairLocation( + calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); + locations->SetInAt(1, Location::RegisterPairLocation( + calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); + // The runtime helper puts the output in R2,R3. + locations->SetOut(Location::RegisterPairLocation(R2, R3)); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << rem->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { + LocationSummary* locations = rem->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + + switch (rem->GetResultType()) { + case Primitive::kPrimInt: { + Register reg1 = first.As<Register>(); + Register reg2 = second.As<Register>(); + Register temp = locations->GetTemp(0).As<Register>(); + + // temp = reg1 / reg2 (integer division) + // temp = temp * reg2 + // dest = reg1 - temp + __ sdiv(temp, reg1, reg2); + __ mul(temp, temp, reg2); + __ sub(out.As<Register>(), reg1, ShifterOperand(temp)); + break; + } + + case Primitive::kPrimLong: { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); + DCHECK_EQ(R2, out.AsRegisterPairLow<Register>()); + DCHECK_EQ(R3, out.AsRegisterPairHigh<Register>()); + + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pLmod), rem, rem->GetDexPc()); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << rem->GetResultType(); + } +} + void LocationsBuilderARM::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -2034,11 +2231,12 @@ void InstructionCodeGeneratorARM::VisitPhi(HPhi* instruction) { void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot; + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue()); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); // Temporary registers for the write barrier. - if (is_object_type) { + if (needs_write_barrier) { locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); } @@ -2069,7 +2267,7 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr case Primitive::kPrimNot: { Register value = locations->InAt(1).As<Register>(); __ StoreToOffset(kStoreWord, value, obj, offset); - if (field_type == Primitive::kPrimNot) { + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) { Register temp = locations->GetTemp(0).As<Register>(); Register card = locations->GetTemp(1).As<Register>(); codegen_->MarkGCCard(temp, card, obj, value); @@ -2302,10 +2500,14 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { Primitive::Type value_type = instruction->GetComponentType(); - bool is_object = value_type == Primitive::kPrimNot; + + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + bool needs_runtime_call = instruction->NeedsTypeCheck(); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( - instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall); - if (is_object) { + instruction, needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall); + if (needs_runtime_call) { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); @@ -2314,6 +2516,12 @@ void LocationsBuilderARM::VisitArraySet(HArraySet* instruction) { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); locations->SetInAt(2, Location::RequiresRegister()); + + if (needs_write_barrier) { + // Temporary registers for the write barrier. + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + } } } @@ -2322,6 +2530,9 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { Register obj = locations->InAt(0).As<Register>(); Location index = locations->InAt(1); Primitive::Type value_type = instruction->GetComponentType(); + bool needs_runtime_call = locations->WillCall(); + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); switch (value_type) { case Primitive::kPrimBoolean: @@ -2352,24 +2563,32 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { break; } - case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); - Register value = locations->InAt(2).As<Register>(); - if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; - __ StoreToOffset(kStoreWord, value, obj, offset); + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + if (!needs_runtime_call) { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + Register value = locations->InAt(2).As<Register>(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + __ StoreToOffset(kStoreWord, value, obj, offset); + } else { + DCHECK(index.IsRegister()) << index; + __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_4)); + __ StoreToOffset(kStoreWord, value, IP, data_offset); + } + if (needs_write_barrier) { + DCHECK_EQ(value_type, Primitive::kPrimNot); + Register temp = locations->GetTemp(0).As<Register>(); + Register card = locations->GetTemp(1).As<Register>(); + codegen_->MarkGCCard(temp, card, obj, value); + } } else { - __ add(IP, obj, ShifterOperand(index.As<Register>(), LSL, TIMES_4)); - __ StoreToOffset(kStoreWord, value, IP, data_offset); + DCHECK_EQ(value_type, Primitive::kPrimNot); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc()); } break; } - case Primitive::kPrimNot: { - codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc()); - break; - } - case Primitive::kPrimLong: { uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); Location value = locations->InAt(2); @@ -2718,11 +2937,12 @@ void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instructi void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); - bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot; + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(instruction->GetFieldType(), instruction->GetValue()); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); // Temporary registers for the write barrier. - if (is_object_type) { + if (needs_write_barrier) { locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); } @@ -2753,7 +2973,7 @@ void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instructi case Primitive::kPrimNot: { Register value = locations->InAt(1).As<Register>(); __ StoreToOffset(kStoreWord, value, cls, offset); - if (field_type == Primitive::kPrimNot) { + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) { Register temp = locations->GetTemp(0).As<Register>(); Register card = locations->GetTemp(1).As<Register>(); codegen_->MarkGCCard(temp, card, cls, value); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index acc3fd6a25..c00fac1a37 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -28,7 +28,8 @@ namespace arm { class CodeGeneratorARM; class SlowPathCodeARM; -static constexpr size_t kArmWordSize = 4; +// Use a local definition to prevent copying mistakes. +static constexpr size_t kArmWordSize = kArmPointerSize; static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 }; static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 }; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 887a4efa19..82dced5e4f 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -48,18 +48,28 @@ bool IsFPType(Primitive::Type type) { return type == Primitive::kPrimFloat || type == Primitive::kPrimDouble; } +bool IsIntegralType(Primitive::Type type) { + switch (type) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimLong: + return true; + default: + return false; + } +} + bool Is64BitType(Primitive::Type type) { return type == Primitive::kPrimLong || type == Primitive::kPrimDouble; } // Convenience helpers to ease conversion to and from VIXL operands. +static_assert((SP == 31) && (WSP == 31) && (XZR == 32) && (WZR == 32), + "Unexpected values for register codes."); int VIXLRegCodeFromART(int code) { - // TODO: static check? - DCHECK_EQ(SP, 31); - DCHECK_EQ(WSP, 31); - DCHECK_EQ(XZR, 32); - DCHECK_EQ(WZR, 32); if (code == SP) { return vixl::kSPRegInternalCode; } @@ -70,11 +80,6 @@ int VIXLRegCodeFromART(int code) { } int ARTRegCodeFromVIXL(int code) { - // TODO: static check? - DCHECK_EQ(SP, 31); - DCHECK_EQ(WSP, 31); - DCHECK_EQ(XZR, 32); - DCHECK_EQ(WZR, 32); if (code == vixl::kSPRegInternalCode) { return SP; } @@ -128,6 +133,17 @@ FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) { instr->InputAt(input_index)->GetType()); } +CPURegister OutputCPURegister(HInstruction* instr) { + return IsFPType(instr->GetType()) ? static_cast<CPURegister>(OutputFPRegister(instr)) + : static_cast<CPURegister>(OutputRegister(instr)); +} + +CPURegister InputCPURegisterAt(HInstruction* instr, int index) { + return IsFPType(instr->InputAt(index)->GetType()) + ? static_cast<CPURegister>(InputFPRegisterAt(instr, index)) + : static_cast<CPURegister>(InputRegisterAt(instr, index)); +} + int64_t Int64ConstantFrom(Location location) { HConstant* instr = location.GetConstant(); return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue() @@ -151,14 +167,18 @@ MemOperand StackOperandFrom(Location location) { return MemOperand(sp, location.GetStackIndex()); } -MemOperand HeapOperand(const Register& base, Offset offset) { +MemOperand HeapOperand(const Register& base, size_t offset) { // A heap reference must be 32bit, so fit in a W register. DCHECK(base.IsW()); - return MemOperand(base.X(), offset.SizeValue()); + return MemOperand(base.X(), offset); } -MemOperand HeapOperandFrom(Location location, Primitive::Type type, Offset offset) { - return HeapOperand(RegisterFrom(location, type), offset); +MemOperand HeapOperand(const Register& base, Offset offset) { + return HeapOperand(base, offset.SizeValue()); +} + +MemOperand HeapOperandFrom(Location location, Offset offset) { + return HeapOperand(RegisterFrom(location, Primitive::kPrimNot), offset); } Location LocationFrom(const Register& reg) { @@ -227,7 +247,8 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type retur return ARM64ReturnLocation(return_type); } -#define __ reinterpret_cast<Arm64Assembler*>(codegen->GetAssembler())->vixl_masm_-> +#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()-> +#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, x).Int32Value() class SlowPathCodeARM64 : public SlowPathCode { public: @@ -245,45 +266,125 @@ class SlowPathCodeARM64 : public SlowPathCode { class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction, - Location index_location, - Location length_location) - : instruction_(instruction), - index_location_(index_location), - length_location_(length_location) {} - - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - CodeGeneratorARM64* arm64_codegen = reinterpret_cast<CodeGeneratorARM64*>(codegen); + BoundsCheckSlowPathARM64() {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); + __ Brk(__LINE__); // TODO: Unimplemented BoundsCheckSlowPathARM64. + } + + private: + DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); +}; + +class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); + __ Bind(GetEntryLabel()); + arm64_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pThrowDivZero), instruction_, instruction_->GetDexPc()); + } + + private: + HDivZeroCheck* const instruction_; + DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64); +}; + +class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { + public: + LoadClassSlowPathARM64(HLoadClass* cls, + HInstruction* at, + uint32_t dex_pc, + bool do_clinit) + : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + DCHECK(at->IsLoadClass() || at->IsClinitCheck()); + } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = at_->GetLocations(); + CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); + + __ Bind(GetEntryLabel()); + codegen->SaveLiveRegisters(locations); + InvokeRuntimeCallingConvention calling_convention; - arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(0)), - index_location_, Primitive::kPrimInt); - arm64_codegen->MoveHelper(LocationFrom(calling_convention.GetRegisterAt(1)), - length_location_, Primitive::kPrimInt); - size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowArrayBounds).SizeValue(); - __ Ldr(lr, MemOperand(tr, offset)); - __ Blr(lr); - codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + __ Mov(calling_convention.GetRegisterAt(0).W(), cls_->GetTypeIndex()); + arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1).W()); + int32_t entry_point_offset = do_clinit_ ? QUICK_ENTRY_POINT(pInitializeStaticStorage) + : QUICK_ENTRY_POINT(pInitializeType); + arm64_codegen->InvokeRuntime(entry_point_offset, at_, dex_pc_); + + // Move the class to the desired location. + Location out = locations->Out(); + if (out.IsValid()) { + DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); + Primitive::Type type = at_->GetType(); + arm64_codegen->MoveHelper(out, calling_convention.GetReturnLocation(type), type); + } + + codegen->RestoreLiveRegisters(locations); + __ B(GetExitLabel()); } private: - HBoundsCheck* const instruction_; - const Location index_location_; - const Location length_location_; + // The class this slow path will load. + HLoadClass* const cls_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); + // The instruction where this slow path is happening. + // (Might be the load class or an initialization check). + HInstruction* const at_; + + // The dex PC of `at_`. + const uint32_t dex_pc_; + + // Whether to initialize the class. + const bool do_clinit_; + + DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64); +}; + +class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { + public: + explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); + CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); + + __ Bind(GetEntryLabel()); + codegen->SaveLiveRegisters(locations); + + InvokeRuntimeCallingConvention calling_convention; + arm64_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(0).W()); + __ Mov(calling_convention.GetRegisterAt(1).W(), instruction_->GetStringIndex()); + arm64_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc()); + Primitive::Type type = instruction_->GetType(); + arm64_codegen->MoveHelper(locations->Out(), calling_convention.GetReturnLocation(type), type); + + codegen->RestoreLiveRegisters(locations); + __ B(GetExitLabel()); + } + + private: + HLoadString* const instruction_; + + DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64); }; class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { public: explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); - int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pThrowNullPointer).Int32Value(); - __ Ldr(lr, MemOperand(tr, offset)); - __ Blr(lr); - codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); + arm64_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc()); } private: @@ -298,13 +399,18 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { HBasicBlock* successor) : instruction_(instruction), successor_(successor) {} - virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { - size_t offset = QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pTestSuspend).SizeValue(); + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); __ Bind(GetEntryLabel()); - __ Ldr(lr, MemOperand(tr, offset)); - __ Blr(lr); - codegen->RecordPcInfo(instruction_, instruction_->GetDexPc()); - __ B(GetReturnLabel()); + codegen->SaveLiveRegisters(instruction_->GetLocations()); + arm64_codegen->InvokeRuntime( + QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc()); + codegen->RestoreLiveRegisters(instruction_->GetLocations()); + if (successor_ == nullptr) { + __ B(GetReturnLabel()); + } else { + __ B(arm64_codegen->GetLabelOf(successor_)); + } } vixl::Label* GetReturnLabel() { @@ -324,6 +430,20 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64); }; +class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { + public: + TypeCheckSlowPathARM64() {} + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + __ Bind(GetEntryLabel()); + __ Brk(__LINE__); // TODO: Unimplemented TypeCheckSlowPathARM64. + __ b(GetExitLabel()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64); +}; + #undef __ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type) { @@ -356,11 +476,12 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph) location_builder_(graph, this), instruction_visitor_(graph, this) {} -#define __ reinterpret_cast<Arm64Assembler*>(GetAssembler())->vixl_masm_-> +#undef __ +#define __ GetVIXLAssembler()-> void CodeGeneratorARM64::GenerateFrameEntry() { // TODO: Add proper support for the stack overflow check. - UseScratchRegisterScope temps(assembler_.vixl_masm_); + UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp = temps.AcquireX(); __ Add(temp, sp, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64))); __ Ldr(temp, MemOperand(temp, 0)); @@ -378,7 +499,7 @@ void CodeGeneratorARM64::GenerateFrameEntry() { // ... : other preserved registers. // sp[frame_size - regs_size]: first preserved register. // ... : reserved frame space. - // sp[0] : context pointer. + // sp[0] : current method. } void CodeGeneratorARM64::GenerateFrameExit() { @@ -413,7 +534,7 @@ void CodeGeneratorARM64::Move(HInstruction* instruction, __ Mov(dst, value); } else { DCHECK(location.IsStackSlot() || location.IsDoubleStackSlot()); - UseScratchRegisterScope temps(assembler_.vixl_masm_); + UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp = instruction->IsIntConstant() ? temps.AcquireW() : temps.AcquireX(); __ Mov(temp, value); __ Str(temp, StackOperandFrom(location)); @@ -465,7 +586,7 @@ Location CodeGeneratorARM64::GetStackLocation(HLoadLocal* load) const { } void CodeGeneratorARM64::MarkGCCard(Register object, Register value) { - UseScratchRegisterScope temps(assembler_.vixl_masm_); + UseScratchRegisterScope temps(GetVIXLAssembler()); Register card = temps.AcquireX(); Register temp = temps.AcquireX(); vixl::Label done; @@ -522,6 +643,19 @@ void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg stream << Arm64ManagedRegister::FromDRegister(DRegister(reg)); } +void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) { + if (constant->IsIntConstant() || constant->IsLongConstant()) { + __ Mov(Register(destination), + constant->IsIntConstant() ? constant->AsIntConstant()->GetValue() + : constant->AsLongConstant()->GetValue()); + } else if (constant->IsFloatConstant()) { + __ Fmov(FPRegister(destination), constant->AsFloatConstant()->GetValue()); + } else { + DCHECK(constant->IsDoubleConstant()); + __ Fmov(FPRegister(destination), constant->AsDoubleConstant()->GetValue()); + } +} + void CodeGeneratorARM64::MoveHelper(Location destination, Location source, Primitive::Type type) { @@ -544,13 +678,7 @@ void CodeGeneratorARM64::MoveHelper(Location destination, } else if (source.IsFpuRegister()) { __ Fmov(dst, FPRegisterFrom(source, type)); } else { - HConstant* cst = source.GetConstant(); - if (cst->IsFloatConstant()) { - __ Fmov(dst, cst->AsFloatConstant()->GetValue()); - } else { - DCHECK(cst->IsDoubleConstant()); - __ Fmov(dst, cst->AsDoubleConstant()->GetValue()); - } + MoveConstant(dst, source.GetConstant()); } } else { DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot()); @@ -558,8 +686,21 @@ void CodeGeneratorARM64::MoveHelper(Location destination, __ Str(RegisterFrom(source, type), StackOperandFrom(destination)); } else if (source.IsFpuRegister()) { __ Str(FPRegisterFrom(source, type), StackOperandFrom(destination)); + } else if (source.IsConstant()) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + HConstant* cst = source.GetConstant(); + CPURegister temp; + if (cst->IsIntConstant() || cst->IsLongConstant()) { + temp = cst->IsIntConstant() ? temps.AcquireW() : temps.AcquireX(); + } else { + DCHECK(cst->IsFloatConstant() || cst->IsDoubleConstant()); + temp = cst->IsFloatConstant() ? temps.AcquireS() : temps.AcquireD(); + } + MoveConstant(temp, cst); + __ Str(temp, StackOperandFrom(destination)); } else { - UseScratchRegisterScope temps(assembler_.vixl_masm_); + DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot()); + UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp = destination.IsDoubleStackSlot() ? temps.AcquireX() : temps.AcquireW(); __ Ldr(temp, StackOperandFrom(source)); __ Str(temp, StackOperandFrom(destination)); @@ -568,61 +709,89 @@ void CodeGeneratorARM64::MoveHelper(Location destination, } void CodeGeneratorARM64::Load(Primitive::Type type, - vixl::Register dst, + vixl::CPURegister dst, const vixl::MemOperand& src) { switch (type) { case Primitive::kPrimBoolean: - __ Ldrb(dst, src); + __ Ldrb(Register(dst), src); break; case Primitive::kPrimByte: - __ Ldrsb(dst, src); + __ Ldrsb(Register(dst), src); break; case Primitive::kPrimShort: - __ Ldrsh(dst, src); + __ Ldrsh(Register(dst), src); break; case Primitive::kPrimChar: - __ Ldrh(dst, src); + __ Ldrh(Register(dst), src); break; case Primitive::kPrimInt: case Primitive::kPrimNot: case Primitive::kPrimLong: - DCHECK(dst.Is64Bits() == (type == Primitive::kPrimLong)); - __ Ldr(dst, src); - break; case Primitive::kPrimFloat: case Primitive::kPrimDouble: + DCHECK(dst.Is64Bits() == Is64BitType(type)); + __ Ldr(dst, src); + break; case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << type; } } void CodeGeneratorARM64::Store(Primitive::Type type, - vixl::Register rt, + vixl::CPURegister rt, const vixl::MemOperand& dst) { switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: - __ Strb(rt, dst); + __ Strb(Register(rt), dst); break; case Primitive::kPrimChar: case Primitive::kPrimShort: - __ Strh(rt, dst); + __ Strh(Register(rt), dst); break; case Primitive::kPrimInt: case Primitive::kPrimNot: case Primitive::kPrimLong: - DCHECK(rt.Is64Bits() == (type == Primitive::kPrimLong)); - __ Str(rt, dst); - break; case Primitive::kPrimFloat: case Primitive::kPrimDouble: + DCHECK(rt.Is64Bits() == Is64BitType(type)); + __ Str(rt, dst); + break; case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable type " << type; } } -#undef __ -#define __ GetAssembler()->vixl_masm_-> +void CodeGeneratorARM64::LoadCurrentMethod(vixl::Register current_method) { + DCHECK(current_method.IsW()); + __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset)); +} + +void CodeGeneratorARM64::InvokeRuntime(int32_t entry_point_offset, + HInstruction* instruction, + uint32_t dex_pc) { + __ Ldr(lr, MemOperand(tr, entry_point_offset)); + __ Blr(lr); + RecordPcInfo(instruction, dex_pc); + DCHECK(instruction->IsSuspendCheck() + || instruction->IsBoundsCheck() + || instruction->IsNullCheck() + || instruction->IsDivZeroCheck() + || !IsLeafMethod()); +} + +void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, + vixl::Register class_reg) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register temp = temps.AcquireW(); + __ Ldr(temp, HeapOperand(class_reg, mirror::Class::StatusOffset())); + __ Cmp(temp, mirror::Class::kStatusInitialized); + __ B(lt, slow_path->GetEntryLabel()); + // Even if the initialized flag is set, we may be in a situation where caches are not synced + // properly. Therefore, we do a memory fence. + __ Dmb(InnerShareable, BarrierAll); + __ Bind(slow_path->GetExitLabel()); +} InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, CodeGeneratorARM64* codegen) @@ -631,27 +800,14 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, codegen_(codegen) {} #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ - M(And) \ - M(CheckCast) \ - M(ClinitCheck) \ - M(DivZeroCheck) \ - M(InstanceOf) \ - M(InvokeInterface) \ - M(LoadClass) \ - M(LoadException) \ - M(LoadString) \ - M(MonitorOperation) \ - M(Or) \ M(ParallelMove) \ - M(StaticFieldGet) \ - M(StaticFieldSet) \ - M(Throw) \ - M(TypeConversion) \ - M(Xor) \ + M(Rem) #define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode enum UnimplementedInstructionBreakCode { + // Using a base helps identify when we hit such breakpoints. + UnimplementedInstructionBreakCodeBaseCode = 0x900, #define ENUM_UNIMPLEMENTED_INSTRUCTION(name) UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name), FOR_EACH_UNIMPLEMENTED_INSTRUCTION(ENUM_UNIMPLEMENTED_INSTRUCTION) #undef ENUM_UNIMPLEMENTED_INSTRUCTION @@ -670,9 +826,9 @@ enum UnimplementedInstructionBreakCode { #undef DEFINE_UNIMPLEMENTED_INSTRUCTION_VISITORS #undef UNIMPLEMENTED_INSTRUCTION_BREAK_CODE +#undef FOR_EACH_UNIMPLEMENTED_INSTRUCTION -void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) { - DCHECK(instr->IsAdd() || instr->IsSub()); +void LocationsBuilderARM64::HandleBinaryOp(HBinaryOperation* instr) { DCHECK_EQ(instr->InputCount(), 2U); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); Primitive::Type type = instr->GetResultType(); @@ -688,7 +844,7 @@ void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) { case Primitive::kPrimDouble: locations->SetInAt(0, Location::RequiresFpuRegister()); locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: @@ -696,9 +852,7 @@ void LocationsBuilderARM64::HandleAddSub(HBinaryOperation* instr) { } } -void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) { - DCHECK(instr->IsAdd() || instr->IsSub()); - +void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { Primitive::Type type = instr->GetType(); switch (type) { @@ -709,8 +863,15 @@ void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) { Operand rhs = InputOperandAt(instr, 1); if (instr->IsAdd()) { __ Add(dst, lhs, rhs); - } else { + } else if (instr->IsAnd()) { + __ And(dst, lhs, rhs); + } else if (instr->IsOr()) { + __ Orr(dst, lhs, rhs); + } else if (instr->IsSub()) { __ Sub(dst, lhs, rhs); + } else { + DCHECK(instr->IsXor()); + __ Eor(dst, lhs, rhs); } break; } @@ -721,22 +882,32 @@ void InstructionCodeGeneratorARM64::HandleAddSub(HBinaryOperation* instr) { FPRegister rhs = InputFPRegisterAt(instr, 1); if (instr->IsAdd()) { __ Fadd(dst, lhs, rhs); - } else { + } else if (instr->IsSub()) { __ Fsub(dst, lhs, rhs); + } else { + LOG(FATAL) << "Unexpected floating-point binary operation"; } break; } default: - LOG(FATAL) << "Unexpected add/sub type " << type; + LOG(FATAL) << "Unexpected binary operation type " << type; } } void LocationsBuilderARM64::VisitAdd(HAdd* instruction) { - HandleAddSub(instruction); + HandleBinaryOp(instruction); } void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) { - HandleAddSub(instruction); + HandleBinaryOp(instruction); +} + +void LocationsBuilderARM64::VisitAnd(HAnd* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { + HandleBinaryOp(instruction); } void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) { @@ -751,11 +922,10 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); Primitive::Type type = instruction->GetType(); Register obj = InputRegisterAt(instruction, 0); - Register out = OutputRegister(instruction); Location index = locations->InAt(1); size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(); MemOperand source(obj); - UseScratchRegisterScope temps(GetAssembler()->vixl_masm_); + UseScratchRegisterScope temps(GetVIXLAssembler()); if (index.IsConstant()) { offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type); @@ -767,7 +937,7 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { source = MemOperand(temp, offset); } - codegen_->Load(type, out, source); + codegen_->Load(type, OutputCPURegister(instruction), source); } void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { @@ -801,18 +971,16 @@ void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) { void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { Primitive::Type value_type = instruction->GetComponentType(); if (value_type == Primitive::kPrimNot) { - __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAputObject).Int32Value())); - __ Blr(lr); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - DCHECK(!codegen_->IsLeafMethod()); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc()); + } else { LocationSummary* locations = instruction->GetLocations(); Register obj = InputRegisterAt(instruction, 0); - Register value = InputRegisterAt(instruction, 2); + CPURegister value = InputCPURegisterAt(instruction, 2); Location index = locations->InAt(1); size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value(); MemOperand destination(obj); - UseScratchRegisterScope temps(GetAssembler()->vixl_masm_); + UseScratchRegisterScope temps(GetVIXLAssembler()); if (index.IsConstant()) { offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type); @@ -828,6 +996,66 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { } } +void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) { + BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64(); + codegen_->AddSlowPath(slow_path); + + __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1)); + __ B(slow_path->GetEntryLabel(), hs); +} + +void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( + instruction, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + Register obj = InputRegisterAt(instruction, 0);; + Register cls = InputRegisterAt(instruction, 1);; + Register temp = temps.AcquireW(); + + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(); + codegen_->AddSlowPath(slow_path); + + // TODO: avoid this check if we know obj is not null. + __ Cbz(obj, slow_path->GetExitLabel()); + // Compare the class of `obj` with `cls`. + __ Ldr(temp, HeapOperand(obj, mirror::Object::ClassOffset())); + __ Cmp(temp, cls); + __ B(ne, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); +} + +void LocationsBuilderARM64::VisitClinitCheck(HClinitCheck* check) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath); + locations->SetInAt(0, Location::RequiresRegister()); + if (check->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitClinitCheck(HClinitCheck* check) { + // We assume the class is not null. + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( + check->GetLoadClass(), check, check->GetDexPc(), true); + codegen_->AddSlowPath(slow_path); + GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0)); +} + void LocationsBuilderARM64::VisitCompare(HCompare* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -846,7 +1074,7 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* instruction) { Register result = OutputRegister(instruction); Register left = InputRegisterAt(instruction, 0); Operand right = InputOperandAt(instruction, 1); - __ Subs(result, left, right); + __ Subs(result.X(), left, right); __ B(eq, &done); __ Mov(result, 1); __ Cneg(result, result, le); @@ -893,6 +1121,7 @@ void InstructionCodeGeneratorARM64::VisitCondition(HCondition* instruction) { void LocationsBuilderARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } \ void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { VisitCondition(comp); } FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS) +#undef DEFINE_CONDITION_VISITORS #undef FOR_EACH_CONDITION_INSTRUCTION void LocationsBuilderARM64::VisitDiv(HDiv* div) { @@ -936,6 +1165,33 @@ void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) { } } +void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); + if (instruction->HasUses()) { + locations->SetOut(Location::SameAsFirstInput()); + } +} + +void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) { + SlowPathCodeARM64* slow_path = + new (GetGraph()->GetArena()) DivZeroCheckSlowPathARM64(instruction); + codegen_->AddSlowPath(slow_path); + Location value = instruction->GetLocations()->InAt(0); + + if (value.IsConstant()) { + int64_t divisor = Int64ConstantFrom(value); + if (divisor == 0) { + __ B(slow_path->GetEntryLabel()); + } else { + LOG(FATAL) << "Divisions by non-null constants should have been optimized away."; + } + } else { + __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel()); + } +} + void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant, LocationSummary::kNoCall); @@ -955,7 +1211,7 @@ void InstructionCodeGeneratorARM64::VisitExit(HExit* exit) { UNUSED(exit); if (kIsDebugBuild) { down_cast<Arm64Assembler*>(GetAssembler())->Comment("Unreachable"); - __ Brk(0); // TODO: Introduce special markers for such code locations. + __ Brk(__LINE__); // TODO: Introduce special markers for such code locations. } } @@ -1038,7 +1294,7 @@ void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { MemOperand field = MemOperand(InputRegisterAt(instruction, 0), instruction->GetFieldOffset().Uint32Value()); - codegen_->Load(instruction->GetType(), OutputRegister(instruction), field); + codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), field); } void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { @@ -1049,12 +1305,54 @@ void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { Primitive::Type field_type = instruction->GetFieldType(); - Register value = InputRegisterAt(instruction, 1); + CPURegister value = InputCPURegisterAt(instruction, 1); Register obj = InputRegisterAt(instruction, 0); codegen_->Store(field_type, value, MemOperand(obj, instruction->GetFieldOffset().Uint32Value())); if (field_type == Primitive::kPrimNot) { - codegen_->MarkGCCard(obj, value); + codegen_->MarkGCCard(obj, Register(value)); + } +} + +void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { + LocationSummary::CallKind call_kind = + instruction->IsClassFinal() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), true); // The output does overlap inputs. +} + +void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Register obj = InputRegisterAt(instruction, 0);; + Register cls = InputRegisterAt(instruction, 1);; + Register out = OutputRegister(instruction); + + vixl::Label done; + + // Return 0 if `obj` is null. + // TODO: Avoid this check if we know `obj` is not null. + __ Mov(out, 0); + __ Cbz(obj, &done); + + // Compare the class of `obj` with `cls`. + __ Ldr(out, MemOperand(obj, mirror::Object::ClassOffset().Int32Value())); + __ Cmp(out, cls); + if (instruction->IsClassFinal()) { + // Classes must be equal for the instanceof to succeed. + __ Cset(out, eq); + } else { + // If the classes are not equal, we go into a slow path. + DCHECK(locations->OnlyCallsOnSlowPath()); + SlowPathCodeARM64* slow_path = + new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(); + codegen_->AddSlowPath(slow_path); + __ B(ne, slow_path->GetEntryLabel()); + __ Mov(out, 1); + __ Bind(slow_path->GetExitLabel()); } + + __ Bind(&done); } void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) { @@ -1067,14 +1365,6 @@ void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant) { UNUSED(constant); } -void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) { - HandleInvoke(invoke); -} - -void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { - HandleInvoke(invoke); -} - void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); @@ -1092,6 +1382,50 @@ void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { } } +void LocationsBuilderARM64::VisitInvokeInterface(HInvokeInterface* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invoke) { + // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError. + Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0)); + uint32_t method_offset = mirror::Class::EmbeddedImTableOffset().Uint32Value() + + (invoke->GetImtIndex() % mirror::Class::kImtSize) * sizeof(mirror::Class::ImTableEntry); + Location receiver = invoke->GetLocations()->InAt(0); + Offset class_offset = mirror::Object::ClassOffset(); + Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize); + + // The register ip1 is required to be used for the hidden argument in + // art_quick_imt_conflict_trampoline, so prevent VIXL from using it. + UseScratchRegisterScope scratch_scope(GetVIXLAssembler()); + scratch_scope.Exclude(ip1); + __ Mov(ip1, invoke->GetDexMethodIndex()); + + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ Ldr(temp, StackOperandFrom(receiver)); + __ Ldr(temp, HeapOperand(temp, class_offset)); + } else { + __ Ldr(temp, HeapOperandFrom(receiver, class_offset)); + } + // temp = temp->GetImtEntryAt(method_offset); + __ Ldr(temp, HeapOperand(temp, method_offset)); + // lr = temp->GetEntryPoint(); + __ Ldr(lr, HeapOperand(temp, entry_point)); + // lr(); + __ Blr(lr); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) { + HandleInvoke(invoke); +} + void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) { Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0)); // Make sure that ArtMethod* is passed in W0 as per the calling convention @@ -1107,7 +1441,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) { // Currently we implement the app -> app logic, which looks up in the resolve cache. // temp = method; - __ Ldr(temp, MemOperand(sp, kCurrentMethodStackOffset)); + codegen_->LoadCurrentMethod(temp); // temp = temp->dex_cache_resolved_methods_; __ Ldr(temp, MemOperand(temp.X(), mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); @@ -1115,7 +1449,8 @@ void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) { __ Ldr(temp, MemOperand(temp.X(), index_in_cache)); // lr = temp->entry_point_from_quick_compiled_code_; __ Ldr(lr, MemOperand(temp.X(), - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64WordSize).SizeValue())); // lr(); __ Blr(lr); @@ -1130,7 +1465,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); Offset class_offset = mirror::Object::ClassOffset(); - Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(); + Offset entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64WordSize); // temp = object->GetClass(); if (receiver.IsStackSlot()) { @@ -1138,8 +1473,7 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { __ Ldr(temp.W(), MemOperand(temp, class_offset.SizeValue())); } else { DCHECK(receiver.IsRegister()); - __ Ldr(temp.W(), HeapOperandFrom(receiver, Primitive::kPrimNot, - class_offset)); + __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset)); } // temp = temp->GetMethodAt(method_offset); __ Ldr(temp.W(), MemOperand(temp, method_offset)); @@ -1151,6 +1485,50 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { + LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { + Register out = OutputRegister(cls); + if (cls->IsReferrersClass()) { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + codegen_->LoadCurrentMethod(out); + __ Ldr(out, HeapOperand(out, mirror::ArtMethod::DeclaringClassOffset())); + } else { + DCHECK(cls->CanCallRuntime()); + codegen_->LoadCurrentMethod(out); + __ Ldr(out, HeapOperand(out, mirror::ArtMethod::DexCacheResolvedTypesOffset())); + __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); + + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + __ Cbz(out, slow_path->GetEntryLabel()); + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); + } + } +} + +void LocationsBuilderARM64::VisitLoadException(HLoadException* load) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kNoCall); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitLoadException(HLoadException* instruction) { + MemOperand exception = MemOperand(tr, Thread::ExceptionOffset<kArm64WordSize>().Int32Value()); + __ Ldr(OutputRegister(instruction), exception); + __ Str(wzr, exception); +} + void LocationsBuilderARM64::VisitLoadLocal(HLoadLocal* load) { load->SetLocations(nullptr); } @@ -1160,6 +1538,24 @@ void InstructionCodeGeneratorARM64::VisitLoadLocal(HLoadLocal* load) { UNUSED(load); } +void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(load, LocationSummary::kCallOnSlowPath); + locations->SetOut(Location::RequiresRegister()); +} + +void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load); + codegen_->AddSlowPath(slow_path); + + Register out = OutputRegister(load); + codegen_->LoadCurrentMethod(out); + __ Ldr(out, HeapOperand(out, mirror::ArtMethod::DexCacheStringsOffset())); + __ Ldr(out, MemOperand(out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex()))); + __ Cbz(out, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); +} + void LocationsBuilderARM64::VisitLocal(HLocal* local) { local->SetLocations(nullptr); } @@ -1178,6 +1574,20 @@ void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant) { UNUSED(constant); } +void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); +} + +void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) { + codegen_->InvokeRuntime(instruction->IsEnter() + ? QUICK_ENTRY_POINT(pLockObject) : QUICK_ENTRY_POINT(pUnlockObject), + instruction, + instruction->GetDexPc()); +} + void LocationsBuilderARM64::VisitMul(HMul* mul) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(mul, LocationSummary::kNoCall); @@ -1193,7 +1603,7 @@ void LocationsBuilderARM64::VisitMul(HMul* mul) { case Primitive::kPrimDouble: locations->SetInAt(0, Location::RequiresFpuRegister()); locations->SetInAt(1, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: @@ -1223,15 +1633,15 @@ void LocationsBuilderARM64::VisitNeg(HNeg* neg) { new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); switch (neg->GetResultType()) { case Primitive::kPrimInt: - case Primitive::kPrimLong: { + case Primitive::kPrimLong: locations->SetInAt(0, Location::RegisterOrConstant(neg->InputAt(0))); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); break; - } case Primitive::kPrimFloat: case Primitive::kPrimDouble: - LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); break; default: @@ -1248,7 +1658,7 @@ void InstructionCodeGeneratorARM64::VisitNeg(HNeg* neg) { case Primitive::kPrimFloat: case Primitive::kPrimDouble: - LOG(FATAL) << "Not yet implemented neg type " << neg->GetResultType(); + __ Fneg(OutputFPRegister(neg), InputFPRegisterAt(neg, 0)); break; default: @@ -1273,14 +1683,10 @@ void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) { DCHECK(type_index.Is(w0)); Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot); DCHECK(current_method.Is(w1)); - __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset)); + codegen_->LoadCurrentMethod(current_method); __ Mov(type_index, instruction->GetTypeIndex()); - int32_t quick_entrypoint_offset = - QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocArrayWithAccessCheck).Int32Value(); - __ Ldr(lr, MemOperand(tr, quick_entrypoint_offset)); - __ Blr(lr); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - DCHECK(!codegen_->IsLeafMethod()); + codegen_->InvokeRuntime( + QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc()); } void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) { @@ -1298,14 +1704,10 @@ void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) DCHECK(type_index.Is(w0)); Register current_method = RegisterFrom(locations->GetTemp(1), Primitive::kPrimNot); DCHECK(current_method.Is(w1)); - __ Ldr(current_method, MemOperand(sp, kCurrentMethodStackOffset)); + codegen_->LoadCurrentMethod(current_method); __ Mov(type_index, instruction->GetTypeIndex()); - int32_t quick_entrypoint_offset = - QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocObjectWithAccessCheck).Int32Value(); - __ Ldr(lr, MemOperand(tr, quick_entrypoint_offset)); - __ Blr(lr); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - DCHECK(!codegen_->IsLeafMethod()); + codegen_->InvokeRuntime( + QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc()); } void LocationsBuilderARM64::VisitNot(HNot* instruction) { @@ -1354,6 +1756,14 @@ void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { } } +void LocationsBuilderARM64::VisitOr(HOr* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorARM64::VisitOr(HOr* instruction) { + HandleBinaryOp(instruction); +} + void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); Location location = parameter_visitor_.GetNextLocation(instruction->GetType()); @@ -1434,31 +1844,43 @@ void InstructionCodeGeneratorARM64::VisitStoreLocal(HStoreLocal* store) { } void LocationsBuilderARM64::VisitSub(HSub* instruction) { - HandleAddSub(instruction); + HandleBinaryOp(instruction); } void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) { - HandleAddSub(instruction); + HandleBinaryOp(instruction); } -void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) { +void LocationsBuilderARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) { + Register cls = InputRegisterAt(instruction, 0); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + codegen_->Load(instruction->GetType(), OutputCPURegister(instruction), MemOperand(cls, offset)); +} + +void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - if (instruction->HasUses()) { - locations->SetOut(Location::SameAsFirstInput()); - } } -void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) { - LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathARM64* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathARM64( - instruction, locations->InAt(0), locations->InAt(1)); - codegen_->AddSlowPath(slow_path); +void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) { + CPURegister value = InputCPURegisterAt(instruction, 1); + Register cls = InputRegisterAt(instruction, 0); + uint32_t offset = instruction->GetFieldOffset().Uint32Value(); + Primitive::Type field_type = instruction->GetFieldType(); - __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1)); - __ B(slow_path->GetEntryLabel(), hs); + codegen_->Store(field_type, value, MemOperand(cls, offset)); + if (field_type == Primitive::kPrimNot) { + codegen_->MarkGCCard(cls, Register(value)); + } } void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) { @@ -1485,5 +1907,74 @@ void InstructionCodeGeneratorARM64::VisitTemporary(HTemporary* temp) { UNUSED(temp); } +void LocationsBuilderARM64::VisitThrow(HThrow* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); +} + +void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) { + codegen_->InvokeRuntime( + QUICK_ENTRY_POINT(pDeliverException), instruction, instruction->GetDexPc()); +} + +void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(conversion, LocationSummary::kNoCall); + Primitive::Type input_type = conversion->GetInputType(); + Primitive::Type result_type = conversion->GetResultType(); + if ((input_type == Primitive::kPrimNot) || (input_type == Primitive::kPrimVoid) || + (result_type == Primitive::kPrimNot) || (result_type == Primitive::kPrimVoid)) { + LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type; + } + + if (IsFPType(input_type)) { + locations->SetInAt(0, Location::RequiresFpuRegister()); + } else { + locations->SetInAt(0, Location::RequiresRegister()); + } + + if (IsFPType(result_type)) { + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); + } else { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } +} + +void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* conversion) { + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + + DCHECK_NE(input_type, result_type); + + if (IsIntegralType(result_type) && IsIntegralType(input_type)) { + int result_size = Primitive::ComponentSize(result_type); + int input_size = Primitive::ComponentSize(input_type); + int min_size = kBitsPerByte * std::min(result_size, input_size); + if ((result_type == Primitive::kPrimChar) || + ((input_type == Primitive::kPrimChar) && (result_size > input_size))) { + __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, min_size); + } else { + __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, min_size); + } + return; + } + + LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type + << " to " << result_type; +} + +void LocationsBuilderARM64::VisitXor(HXor* instruction) { + HandleBinaryOp(instruction); +} + +void InstructionCodeGeneratorARM64::VisitXor(HXor* instruction) { + HandleBinaryOp(instruction); +} + +#undef __ +#undef QUICK_ENTRY_POINT + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 54e87f4d9c..a40f27fafa 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -29,8 +29,11 @@ namespace art { namespace arm64 { class CodeGeneratorARM64; +class SlowPathCodeARM64; + +// Use a local definition to prevent copying mistakes. +static constexpr size_t kArm64WordSize = kArm64PointerSize; -static constexpr size_t kArm64WordSize = 8; static const vixl::Register kParameterCoreRegisters[] = { vixl::x1, vixl::x2, vixl::x3, vixl::x4, vixl::x5, vixl::x6, vixl::x7 }; @@ -103,9 +106,11 @@ class InstructionCodeGeneratorARM64 : public HGraphVisitor { void LoadCurrentMethod(XRegister reg); Arm64Assembler* GetAssembler() const { return assembler_; } + vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; } private: - void HandleAddSub(HBinaryOperation* instr); + void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::Register class_reg); + void HandleBinaryOp(HBinaryOperation* instr); Arm64Assembler* const assembler_; CodeGeneratorARM64* const codegen_; @@ -124,7 +129,7 @@ class LocationsBuilderARM64 : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION private: - void HandleAddSub(HBinaryOperation* instr); + void HandleBinaryOp(HBinaryOperation* instr); void HandleInvoke(HInvoke* instr); CodeGeneratorARM64* const codegen_; @@ -162,9 +167,10 @@ class CodeGeneratorARM64 : public CodeGenerator { return kArm64WordSize; } - uintptr_t GetAddressOf(HBasicBlock* block ATTRIBUTE_UNUSED) const OVERRIDE { - UNIMPLEMENTED(INFO) << "TODO: GetAddressOf"; - return 0u; + uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE { + vixl::Label* block_entry_label = GetLabelOf(block); + DCHECK(block_entry_label->IsBound()); + return block_entry_label->location(); } size_t FrameEntrySpillSize() const OVERRIDE; @@ -172,6 +178,7 @@ class CodeGeneratorARM64 : public CodeGenerator { HGraphVisitor* GetLocationBuilder() OVERRIDE { return &location_builder_; } HGraphVisitor* GetInstructionVisitor() OVERRIDE { return &instruction_visitor_; } Arm64Assembler* GetAssembler() OVERRIDE { return &assembler_; } + vixl::MacroAssembler* GetVIXLAssembler() { return GetAssembler()->vixl_masm_; } // Emit a write barrier. void MarkGCCard(vixl::Register object, vixl::Register value); @@ -185,18 +192,18 @@ class CodeGeneratorARM64 : public CodeGenerator { Location GetStackLocation(HLoadLocal* load) const OVERRIDE; - size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE { + size_t SaveCoreRegister(size_t stack_index, uint32_t reg_id) { UNUSED(stack_index); UNUSED(reg_id); - UNIMPLEMENTED(INFO) << "TODO: SaveCoreRegister"; - return 0; + LOG(INFO) << "CodeGeneratorARM64::SaveCoreRegister()"; + return kArm64WordSize; } - size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) OVERRIDE { + size_t RestoreCoreRegister(size_t stack_index, uint32_t reg_id) { UNUSED(stack_index); UNUSED(reg_id); - UNIMPLEMENTED(INFO) << "TODO: RestoreCoreRegister"; - return 0; + LOG(INFO) << "CodeGeneratorARM64::RestoreCoreRegister()"; + return kArm64WordSize; } // The number of registers that can be allocated. The register allocator may @@ -226,9 +233,14 @@ class CodeGeneratorARM64 : public CodeGenerator { } // Code generation helpers. + void MoveConstant(vixl::CPURegister destination, HConstant* constant); void MoveHelper(Location destination, Location source, Primitive::Type type); - void Load(Primitive::Type type, vixl::Register dst, const vixl::MemOperand& src); - void Store(Primitive::Type type, vixl::Register rt, const vixl::MemOperand& dst); + void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src); + void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst); + void LoadCurrentMethod(vixl::Register current_method); + + // Generate code to invoke a runtime entry point. + void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc); ParallelMoveResolver* GetMoveResolver() OVERRIDE { UNIMPLEMENTED(INFO) << "TODO: MoveResolver"; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 8a8fec2609..3c53cea0bf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -42,6 +42,9 @@ static constexpr size_t kRuntimeParameterCoreRegistersLength = static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { }; static constexpr size_t kRuntimeParameterFpuRegistersLength = 0; +// Marker for places that can be updated once we don't follow the quick ABI. +static constexpr bool kFollowsQuickABI = true; + class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> { public: InvokeRuntimeCallingConvention() @@ -100,19 +103,24 @@ class DivZeroCheckSlowPathX86 : public SlowPathCodeX86 { DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86); }; -class DivMinusOneSlowPathX86 : public SlowPathCodeX86 { +class DivRemMinusOneSlowPathX86 : public SlowPathCodeX86 { public: - explicit DivMinusOneSlowPathX86(Register reg) : reg_(reg) {} + explicit DivRemMinusOneSlowPathX86(Register reg, bool is_div) : reg_(reg), is_div_(is_div) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); - __ negl(reg_); + if (is_div_) { + __ negl(reg_); + } else { + __ movl(reg_, Immediate(0)); + } __ jmp(GetExitLabel()); } private: Register reg_; - DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86); + bool is_div_; + DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86); }; class StackOverflowCheckSlowPathX86 : public SlowPathCodeX86 { @@ -427,6 +435,7 @@ void CodeGeneratorX86::SetupBlockedRegisters() const { blocked_core_registers_[ESP] = true; // TODO: We currently don't use Quick's callee saved registers. + DCHECK(kFollowsQuickABI); blocked_core_registers_[EBP] = true; blocked_core_registers_[ESI] = true; blocked_core_registers_[EDI] = true; @@ -1111,7 +1120,8 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { // temp = temp[index_in_cache] __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()))); // (temp + offset_of_quick_compiled_code)() - __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); + __ call(Address( + temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -1175,7 +1185,8 @@ void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { // temp = temp->GetMethodAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); - __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); + __ call(Address( + temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -1210,7 +1221,8 @@ void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) // temp = temp->GetImtEntryAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); - __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kX86WordSize).Int32Value())); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -1306,6 +1318,22 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { } break; + case Primitive::kPrimShort: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-short' instruction. + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + case Primitive::kPrimInt: switch (input_type) { case Primitive::kPrimLong: @@ -1367,9 +1395,49 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimFloat: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-float' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; + break; + case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-double' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } break; default: @@ -1408,6 +1476,29 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio } break; + case Primitive::kPrimShort: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-short' instruction. + if (in.IsRegister()) { + __ movsxw(out.As<Register>(), in.As<Register>()); + } else if (in.IsStackSlot()) { + __ movsxw(out.As<Register>(), Address(ESP, in.GetStackIndex())); + } else { + DCHECK(in.GetConstant()->IsIntConstant()); + int32_t value = in.GetConstant()->AsIntConstant()->GetValue(); + __ movl(out.As<Register>(), Immediate(static_cast<int16_t>(value))); + } + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + case Primitive::kPrimInt: switch (input_type) { case Primitive::kPrimLong: @@ -1486,9 +1577,47 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio break; case Primitive::kPrimFloat: + switch (input_type) { + // Processing a Dex `int-to-float' instruction. + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + __ cvtsi2ss(out.As<XmmRegister>(), in.As<Register>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; + break; + case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + switch (input_type) { + // Processing a Dex `int-to-double' instruction. + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + __ cvtsi2sd(out.As<XmmRegister>(), in.As<Register>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; break; default: @@ -1753,6 +1882,68 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { } } +void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + + LocationSummary* locations = instruction->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + bool is_div = instruction->IsDiv(); + + switch (instruction->GetResultType()) { + case Primitive::kPrimInt: { + Register second_reg = second.As<Register>(); + DCHECK_EQ(EAX, first.As<Register>()); + DCHECK_EQ(is_div ? EAX : EDX, out.As<Register>()); + + SlowPathCodeX86* slow_path = + new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(out.As<Register>(), is_div); + codegen_->AddSlowPath(slow_path); + + // 0x80000000/-1 triggers an arithmetic exception! + // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so + // it's safe to just use negl instead of more complex comparisons. + + __ cmpl(second_reg, Immediate(-1)); + __ j(kEqual, slow_path->GetEntryLabel()); + + // edx:eax <- sign-extended of eax + __ cdq(); + // eax = quotient, edx = remainder + __ idivl(second_reg); + + __ Bind(slow_path->GetExitLabel()); + break; + } + + case Primitive::kPrimLong: { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); + DCHECK_EQ(EAX, out.AsRegisterPairLow<Register>()); + DCHECK_EQ(EDX, out.AsRegisterPairHigh<Register>()); + + if (is_div) { + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pLdiv))); + } else { + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pLmod))); + } + uint32_t dex_pc = is_div + ? instruction->AsDiv()->GetDexPc() + : instruction->AsRem()->GetDexPc(); + codegen_->RecordPcInfo(instruction, dex_pc); + + break; + } + + default: + LOG(FATAL) << "Unexpected type for GenerateDivRemIntegral " << instruction->GetResultType(); + } +} + void LocationsBuilderX86::VisitDiv(HDiv* div) { LocationSummary::CallKind call_kind = div->GetResultType() == Primitive::kPrimLong ? LocationSummary::kCall @@ -1798,45 +1989,9 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) { Location second = locations->InAt(1); switch (div->GetResultType()) { - case Primitive::kPrimInt: { - DCHECK(first.Equals(out)); - Register first_reg = first.As<Register>(); - Register second_reg = second.As<Register>(); - DCHECK_EQ(EAX, first_reg); - DCHECK_EQ(EDX, locations->GetTemp(0).As<Register>()); - - SlowPathCodeX86* slow_path = - new (GetGraph()->GetArena()) DivMinusOneSlowPathX86(first_reg); - codegen_->AddSlowPath(slow_path); - - // 0x80000000/-1 triggers an arithmetic exception! - // Dividing by -1 is actually negation and -0x800000000 = 0x80000000 so - // it's safe to just use negl instead of more complex comparisons. - - __ cmpl(second_reg, Immediate(-1)); - __ j(kEqual, slow_path->GetEntryLabel()); - - // edx:eax <- sign-extended of eax - __ cdq(); - // eax = quotient, edx = remainder - __ idivl(second_reg); - - __ Bind(slow_path->GetExitLabel()); - break; - } - + case Primitive::kPrimInt: case Primitive::kPrimLong: { - InvokeRuntimeCallingConvention calling_convention; - DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>()); - DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>()); - DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegisterPairLow<Register>()); - DCHECK_EQ(calling_convention.GetRegisterAt(3), second.AsRegisterPairHigh<Register>()); - DCHECK_EQ(EAX, out.AsRegisterPairLow<Register>()); - DCHECK_EQ(EDX, out.AsRegisterPairHigh<Register>()); - - __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pLdiv))); - codegen_->RecordPcInfo(div, div->GetDexPc()); - + GenerateDivRemIntegral(div); break; } @@ -1857,6 +2012,58 @@ void InstructionCodeGeneratorX86::VisitDiv(HDiv* div) { } } +void LocationsBuilderX86::VisitRem(HRem* rem) { + LocationSummary::CallKind call_kind = rem->GetResultType() == Primitive::kPrimLong + ? LocationSummary::kCall + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); + + switch (rem->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RegisterLocation(EAX)); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RegisterLocation(EDX)); + break; + } + case Primitive::kPrimLong: { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterPairLocation( + calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1))); + locations->SetInAt(1, Location::RegisterPairLocation( + calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3))); + // Runtime helper puts the result in EAX, EDX. + locations->SetOut(Location::RegisterPairLocation(EAX, EDX)); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << rem->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::VisitRem(HRem* rem) { + Primitive::Type type = rem->GetResultType(); + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GenerateDivRemIntegral(rem); + break; + } + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + LOG(FATAL) << "Unimplemented rem type " << type; + break; + } + default: + LOG(FATAL) << "Unexpected rem type " << type; + } +} + void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -2081,7 +2288,9 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); Primitive::Type field_type = instruction->GetFieldType(); - bool is_object_type = field_type == Primitive::kPrimNot; + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); + bool is_byte_type = (field_type == Primitive::kPrimBoolean) || (field_type == Primitive::kPrimByte); // The register allocator does not support multiple @@ -2093,7 +2302,7 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) locations->SetInAt(1, Location::RequiresRegister()); } // Temporary registers for the write barrier. - if (is_object_type) { + if (needs_write_barrier) { locations->AddTemp(Location::RequiresRegister()); // Ensure the card is in a byte register. locations->AddTemp(Location::RegisterLocation(ECX)); @@ -2126,7 +2335,7 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr Register value = locations->InAt(1).As<Register>(); __ movl(Address(obj, offset), value); - if (field_type == Primitive::kPrimNot) { + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { Register temp = locations->GetTemp(0).As<Register>(); Register card = locations->GetTemp(1).As<Register>(); codegen_->MarkGCCard(temp, card, obj, value); @@ -2372,11 +2581,20 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { Primitive::Type value_type = instruction->GetComponentType(); + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + + DCHECK(kFollowsQuickABI); + bool not_enough_registers = needs_write_barrier + && !instruction->GetValue()->IsConstant() + && !instruction->GetIndex()->IsConstant(); + bool needs_runtime_call = instruction->NeedsTypeCheck() || not_enough_registers; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( instruction, - value_type == Primitive::kPrimNot ? LocationSummary::kCall : LocationSummary::kNoCall); + needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall); - if (value_type == Primitive::kPrimNot) { + if (needs_runtime_call) { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); @@ -2395,6 +2613,12 @@ void LocationsBuilderX86::VisitArraySet(HArraySet* instruction) { } else { locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2))); } + // Temporary registers for the write barrier. + if (needs_write_barrier) { + locations->AddTemp(Location::RequiresRegister()); + // Ensure the card is in a byte register. + locations->AddTemp(Location::RegisterLocation(ECX)); + } } } @@ -2404,6 +2628,9 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { Location index = locations->InAt(1); Location value = locations->InAt(2); Primitive::Type value_type = instruction->GetComponentType(); + bool needs_runtime_call = locations->WillCall(); + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); switch (value_type) { case Primitive::kPrimBoolean: @@ -2452,34 +2679,45 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { break; } - case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); - if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; - if (value.IsRegister()) { - __ movl(Address(obj, offset), value.As<Register>()); + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + if (!needs_runtime_call) { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + if (index.IsConstant()) { + size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + if (value.IsRegister()) { + __ movl(Address(obj, offset), value.As<Register>()); + } else { + DCHECK(value.IsConstant()) << value; + __ movl(Address(obj, offset), + Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + } } else { - __ movl(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + DCHECK(index.IsRegister()) << index; + if (value.IsRegister()) { + __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset), + value.As<Register>()); + } else { + DCHECK(value.IsConstant()) << value; + __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset), + Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + } } - } else { - if (value.IsRegister()) { - __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset), - value.As<Register>()); - } else { - __ movl(Address(obj, index.As<Register>(), TIMES_4, data_offset), - Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + + if (needs_write_barrier) { + Register temp = locations->GetTemp(0).As<Register>(); + Register card = locations->GetTemp(1).As<Register>(); + codegen_->MarkGCCard(temp, card, obj, value.As<Register>()); } + } else { + DCHECK_EQ(value_type, Primitive::kPrimNot); + DCHECK(!codegen_->IsLeafMethod()); + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject))); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } break; } - case Primitive::kPrimNot: { - DCHECK(!codegen_->IsLeafMethod()); - __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAputObject))); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - break; - } - case Primitive::kPrimLong: { uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); if (index.IsConstant()) { @@ -2844,7 +3082,8 @@ void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) { new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); Primitive::Type field_type = instruction->GetFieldType(); - bool is_object_type = field_type == Primitive::kPrimNot; + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); bool is_byte_type = (field_type == Primitive::kPrimBoolean) || (field_type == Primitive::kPrimByte); // The register allocator does not support multiple @@ -2856,7 +3095,7 @@ void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) { locations->SetInAt(1, Location::RequiresRegister()); } // Temporary registers for the write barrier. - if (is_object_type) { + if (needs_write_barrier) { locations->AddTemp(Location::RequiresRegister()); // Ensure the card is in a byte register. locations->AddTemp(Location::RegisterLocation(ECX)); @@ -2889,7 +3128,7 @@ void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instructi Register value = locations->InAt(1).As<Register>(); __ movl(Address(cls, offset), value); - if (field_type == Primitive::kPrimNot) { + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { Register temp = locations->GetTemp(0).As<Register>(); Register card = locations->GetTemp(1).As<Register>(); codegen_->MarkGCCard(temp, card, cls, value); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 841b28b158..0aff6cc493 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -25,7 +25,8 @@ namespace art { namespace x86 { -static constexpr size_t kX86WordSize = 4; +// Use a local definition to prevent copying mistakes. +static constexpr size_t kX86WordSize = kX86PointerSize; class CodeGeneratorX86; class SlowPathCodeX86; @@ -130,6 +131,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg); void HandleBitwiseOperation(HBinaryOperation* instruction); + void GenerateDivRemIntegral(HBinaryOperation* instruction); X86Assembler* const assembler_; CodeGeneratorX86* const codegen_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 5aa1c4a6c8..97f5e5c7ac 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -106,26 +106,36 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCodeX86_64 { DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64); }; -class DivMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 { +class DivRemMinusOneSlowPathX86_64 : public SlowPathCodeX86_64 { public: - explicit DivMinusOneSlowPathX86_64(Register reg, Primitive::Type type) - : reg_(reg), type_(type) {} + explicit DivRemMinusOneSlowPathX86_64(Register reg, Primitive::Type type, bool is_div) + : cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {} virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); if (type_ == Primitive::kPrimInt) { - __ negl(CpuRegister(reg_)); + if (is_div_) { + __ negl(cpu_reg_); + } else { + __ movl(cpu_reg_, Immediate(0)); + } + } else { DCHECK_EQ(Primitive::kPrimLong, type_); - __ negq(CpuRegister(reg_)); + if (is_div_) { + __ negq(cpu_reg_); + } else { + __ movq(cpu_reg_, Immediate(0)); + } } __ jmp(GetExitLabel()); } private: - const Register reg_; + const CpuRegister cpu_reg_; const Primitive::Type type_; - DISALLOW_COPY_AND_ASSIGN(DivMinusOneSlowPathX86_64); + const bool is_div_; + DISALLOW_COPY_AND_ASSIGN(DivRemMinusOneSlowPathX86_64); }; class StackOverflowCheckSlowPathX86_64 : public SlowPathCodeX86_64 { @@ -1102,7 +1112,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { // temp = temp[index_in_cache] __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()))); // (temp + offset_of_quick_compiled_code)() - __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kX86_64WordSize).SizeValue())); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -1161,7 +1172,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) // temp = temp->GetMethodAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); - __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kX86_64WordSize).SizeValue())); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -1196,7 +1208,8 @@ void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invo // temp = temp->GetImtEntryAt(method_offset); __ movl(temp, Address(temp, method_offset)); // call temp->GetEntryPoint(); - __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kX86_64WordSize).SizeValue())); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -1304,6 +1317,22 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { } break; + case Primitive::kPrimShort: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-short' instruction. + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + case Primitive::kPrimInt: switch (input_type) { case Primitive::kPrimLong: @@ -1367,9 +1396,49 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimFloat: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-float' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; + break; + case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-double' instruction. + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } break; default: @@ -1409,6 +1478,30 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver } break; + case Primitive::kPrimShort: + switch (input_type) { + case Primitive::kPrimByte: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + // Processing a Dex `int-to-short' instruction. + if (in.IsRegister()) { + __ movsxw(out.As<CpuRegister>(), in.As<CpuRegister>()); + } else if (in.IsStackSlot()) { + __ movsxw(out.As<CpuRegister>(), + Address(CpuRegister(RSP), in.GetStackIndex())); + } else { + DCHECK(in.GetConstant()->IsIntConstant()); + __ movl(out.As<CpuRegister>(), + Immediate(static_cast<int16_t>(in.GetConstant()->AsIntConstant()->GetValue()))); + } + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + } + break; + case Primitive::kPrimInt: switch (input_type) { case Primitive::kPrimLong: @@ -1488,9 +1581,47 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver break; case Primitive::kPrimFloat: + switch (input_type) { + // Processing a Dex `int-to-float' instruction. + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + __ cvtsi2ss(out.As<XmmRegister>(), in.As<CpuRegister>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; + break; + case Primitive::kPrimDouble: - LOG(FATAL) << "Type conversion from " << input_type - << " to " << result_type << " not yet implemented"; + switch (input_type) { + // Processing a Dex `int-to-double' instruction. + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimChar: + __ cvtsi2sd(out.As<XmmRegister>(), in.As<CpuRegister>()); + break; + + case Primitive::kPrimLong: + case Primitive::kPrimFloat: + LOG(FATAL) << "Type conversion from " << input_type + << " to " << result_type << " not yet implemented"; + break; + + default: + LOG(FATAL) << "Unexpected type conversion from " << input_type + << " to " << result_type; + }; break; default: @@ -1701,6 +1832,47 @@ void InstructionCodeGeneratorX86_64::VisitMul(HMul* mul) { } } +void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* instruction) { + DCHECK(instruction->IsDiv() || instruction->IsRem()); + Primitive::Type type = instruction->GetResultType(); + DCHECK(type == Primitive::kPrimInt || Primitive::kPrimLong); + + bool is_div = instruction->IsDiv(); + LocationSummary* locations = instruction->GetLocations(); + + CpuRegister out_reg = locations->Out().As<CpuRegister>(); + CpuRegister second_reg = locations->InAt(1).As<CpuRegister>(); + + DCHECK_EQ(RAX, locations->InAt(0).As<CpuRegister>().AsRegister()); + DCHECK_EQ(is_div ? RAX : RDX, out_reg.AsRegister()); + + SlowPathCodeX86_64* slow_path = + new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64( + out_reg.AsRegister(), type, is_div); + codegen_->AddSlowPath(slow_path); + + // 0x80000000(00000000)/-1 triggers an arithmetic exception! + // Dividing by -1 is actually negation and -0x800000000(00000000) = 0x80000000(00000000) + // so it's safe to just use negl instead of more complex comparisons. + + __ cmpl(second_reg, Immediate(-1)); + __ j(kEqual, slow_path->GetEntryLabel()); + + if (type == Primitive::kPrimInt) { + // edx:eax <- sign-extended of eax + __ cdq(); + // eax = quotient, edx = remainder + __ idivl(second_reg); + } else { + // rdx:rax <- sign-extended of rax + __ cqo(); + // rax = quotient, rdx = remainder + __ idivq(second_reg); + } + + __ Bind(slow_path->GetExitLabel()); +} + void LocationsBuilderX86_64::VisitDiv(HDiv* div) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, LocationSummary::kNoCall); @@ -1738,35 +1910,7 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) { switch (type) { case Primitive::kPrimInt: case Primitive::kPrimLong: { - CpuRegister first_reg = first.As<CpuRegister>(); - CpuRegister second_reg = second.As<CpuRegister>(); - DCHECK_EQ(RAX, first_reg.AsRegister()); - DCHECK_EQ(RDX, locations->GetTemp(0).As<CpuRegister>().AsRegister()); - - SlowPathCodeX86_64* slow_path = - new (GetGraph()->GetArena()) DivMinusOneSlowPathX86_64(first_reg.AsRegister(), type); - codegen_->AddSlowPath(slow_path); - - // 0x80000000(00000000)/-1 triggers an arithmetic exception! - // Dividing by -1 is actually negation and -0x800000000(00000000) = 0x80000000(00000000) - // so it's safe to just use negl instead of more complex comparisons. - - __ cmpl(second_reg, Immediate(-1)); - __ j(kEqual, slow_path->GetEntryLabel()); - - if (type == Primitive::kPrimInt) { - // edx:eax <- sign-extended of eax - __ cdq(); - // eax = quotient, edx = remainder - __ idivl(second_reg); - } else { - // rdx:rax <- sign-extended of rax - __ cqo(); - // rax = quotient, rdx = remainder - __ idivq(second_reg); - } - - __ Bind(slow_path->GetExitLabel()); + GenerateDivRemIntegral(div); break; } @@ -1785,6 +1929,50 @@ void InstructionCodeGeneratorX86_64::VisitDiv(HDiv* div) { } } +void LocationsBuilderX86_64::VisitRem(HRem* rem) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(rem, LocationSummary::kNoCall); + switch (rem->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RegisterLocation(RAX)); + locations->SetInAt(1, Location::RequiresRegister()); + // Intel uses rdx:rax as the dividend and puts the remainder in rdx + locations->SetOut(Location::RegisterLocation(RDX)); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << rem->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::VisitRem(HRem* rem) { + Primitive::Type type = rem->GetResultType(); + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + GenerateDivRemIntegral(rem); + break; + } + + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + LOG(FATAL) << "Unimplemented rem type " << rem->GetResultType(); + break; + } + + default: + LOG(FATAL) << "Unexpected rem type " << rem->GetResultType(); + } +} + void LocationsBuilderX86_64::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1946,10 +2134,11 @@ void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instructio LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); Primitive::Type field_type = instruction->GetFieldType(); - bool is_object_type = field_type == Primitive::kPrimNot; + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue()); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - if (is_object_type) { + if (needs_write_barrier) { // Temporary registers for the write barrier. locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); @@ -1981,7 +2170,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in case Primitive::kPrimNot: { CpuRegister value = locations->InAt(1).As<CpuRegister>(); __ movl(Address(obj, offset), value); - if (field_type == Primitive::kPrimNot) { + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) { CpuRegister temp = locations->GetTemp(0).As<CpuRegister>(); CpuRegister card = locations->GetTemp(1).As<CpuRegister>(); codegen_->MarkGCCard(temp, card, obj, value); @@ -2231,10 +2420,14 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { Primitive::Type value_type = instruction->GetComponentType(); - bool is_object = value_type == Primitive::kPrimNot; + + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + bool needs_runtime_call = instruction->NeedsTypeCheck(); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( - instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall); - if (is_object) { + instruction, needs_runtime_call ? LocationSummary::kCall : LocationSummary::kNoCall); + if (needs_runtime_call) { InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); @@ -2251,6 +2444,12 @@ void LocationsBuilderX86_64::VisitArraySet(HArraySet* instruction) { } else { locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2))); } + + if (needs_write_barrier) { + // Temporary registers for the write barrier. + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + } } } @@ -2260,6 +2459,9 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { Location index = locations->InAt(1); Location value = locations->InAt(2); Primitive::Type value_type = instruction->GetComponentType(); + bool needs_runtime_call = locations->WillCall(); + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); switch (value_type) { case Primitive::kPrimBoolean: @@ -2292,13 +2494,16 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { if (value.IsRegister()) { __ movw(Address(obj, offset), value.As<CpuRegister>()); } else { + DCHECK(value.IsConstant()) << value; __ movw(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } } else { + DCHECK(index.IsRegister()) << index; if (value.IsRegister()) { __ movw(Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset), value.As<CpuRegister>()); } else { + DCHECK(value.IsConstant()) << value; __ movw(Address(obj, index.As<CpuRegister>(), TIMES_2, data_offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue())); } @@ -2306,35 +2511,47 @@ void InstructionCodeGeneratorX86_64::VisitArraySet(HArraySet* instruction) { break; } - case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); - if (index.IsConstant()) { - size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; - if (value.IsRegister()) { - __ movl(Address(obj, offset), value.As<CpuRegister>()); + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + if (!needs_runtime_call) { + uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); + if (index.IsConstant()) { + size_t offset = + (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; + if (value.IsRegister()) { + __ movl(Address(obj, offset), value.As<CpuRegister>()); + } else { + DCHECK(value.IsConstant()) << value; + __ movl(Address(obj, offset), + Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + } } else { - __ movl(Address(obj, offset), Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + DCHECK(index.IsRegister()) << index; + if (value.IsRegister()) { + __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), + value.As<CpuRegister>()); + } else { + DCHECK(value.IsConstant()) << value; + __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), + Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + } } - } else { - if (value.IsRegister()) { - __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), - value.As<CpuRegister>()); - } else { - DCHECK(value.IsConstant()) << value; - __ movl(Address(obj, index.As<CpuRegister>(), TIMES_4, data_offset), - Immediate(value.GetConstant()->AsIntConstant()->GetValue())); + + if (needs_write_barrier) { + DCHECK_EQ(value_type, Primitive::kPrimNot); + CpuRegister temp = locations->GetTemp(0).As<CpuRegister>(); + CpuRegister card = locations->GetTemp(1).As<CpuRegister>(); + codegen_->MarkGCCard(temp, card, obj, value.As<CpuRegister>()); } + } else { + DCHECK_EQ(value_type, Primitive::kPrimNot); + __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true)); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); } break; } - case Primitive::kPrimNot: { - __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAputObject), true)); - DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); - break; - } - case Primitive::kPrimLong: { uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); if (index.IsConstant()) { @@ -2813,10 +3030,11 @@ void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); Primitive::Type field_type = instruction->GetFieldType(); - bool is_object_type = field_type == Primitive::kPrimNot; + bool needs_write_barrier = + CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue()); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - if (is_object_type) { + if (needs_write_barrier) { // Temporary registers for the write barrier. locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); @@ -2848,7 +3066,7 @@ void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instru case Primitive::kPrimNot: { CpuRegister value = locations->InAt(1).As<CpuRegister>(); __ movl(Address(cls, offset), value); - if (field_type == Primitive::kPrimNot) { + if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->GetValue())) { CpuRegister temp = locations->GetTemp(0).As<CpuRegister>(); CpuRegister card = locations->GetTemp(1).As<CpuRegister>(); codegen_->MarkGCCard(temp, card, cls, value); diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 4c6e4750d7..29c679d8f1 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -25,7 +25,8 @@ namespace art { namespace x86_64 { -static constexpr size_t kX86_64WordSize = 8; +// Use a local definition to prevent copying mistakes. +static constexpr size_t kX86_64WordSize = kX86_64PointerSize; static constexpr Register kParameterCoreRegisters[] = { RSI, RDX, RCX, R8, R9 }; static constexpr FloatRegister kParameterFloatRegisters[] = @@ -134,6 +135,7 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor { void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg); void HandleBitwiseOperation(HBinaryOperation* operation); + void GenerateDivRemIntegral(HBinaryOperation* instruction); X86_64Assembler* const assembler_; CodeGeneratorX86_64* const codegen_; diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h index d2acfa6973..ac00824e33 100644 --- a/compiler/optimizing/constant_folding.h +++ b/compiler/optimizing/constant_folding.h @@ -32,10 +32,10 @@ namespace art { */ class HConstantFolding : public HOptimization { public: - HConstantFolding(HGraph* graph, const HGraphVisualizer& visualizer) - : HOptimization(graph, true, kConstantFoldingPassName, visualizer) {} + explicit HConstantFolding(HGraph* graph) + : HOptimization(graph, true, kConstantFoldingPassName) {} - virtual void Run() OVERRIDE; + void Run() OVERRIDE; static constexpr const char* kConstantFoldingPassName = "constant_folding"; diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 856c5165a3..a56b9d9a12 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -47,8 +47,7 @@ static void TestCode(const uint16_t* data, ASSERT_EQ(expected_before, actual_before); x86::CodeGeneratorX86 codegen(graph); - HGraphVisualizer visualizer(nullptr, graph, codegen, ""); - HConstantFolding(graph, visualizer).Run(); + HConstantFolding(graph).Run(); SSAChecker ssa_checker(&allocator, graph); ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); @@ -60,7 +59,7 @@ static void TestCode(const uint16_t* data, check_after_cf(graph); - HDeadCodeElimination(graph, visualizer).Run(); + HDeadCodeElimination(graph).Run(); ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index a4446ae04d..3db2c3ff3f 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -28,10 +28,10 @@ namespace art { */ class HDeadCodeElimination : public HOptimization { public: - HDeadCodeElimination(HGraph* graph, const HGraphVisualizer& visualizer) - : HOptimization(graph, true, kDeadCodeEliminationPassName, visualizer) {} + explicit HDeadCodeElimination(HGraph* graph) + : HOptimization(graph, true, kDeadCodeEliminationPassName) {} - virtual void Run() OVERRIDE; + void Run() OVERRIDE; static constexpr const char* kDeadCodeEliminationPassName = "dead_code_elimination"; diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 0c6807482a..5d4b9cb024 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -41,8 +41,7 @@ static void TestCode(const uint16_t* data, ASSERT_EQ(actual_before, expected_before); x86::CodeGeneratorX86 codegen(graph); - HGraphVisualizer visualizer(nullptr, graph, codegen, ""); - HDeadCodeElimination(graph, visualizer).Run(); + HDeadCodeElimination(graph).Run(); SSAChecker ssa_checker(&allocator, graph); ssa_checker.Run(); ASSERT_TRUE(ssa_checker.IsValid()); diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h index 8d2c77475c..a841d5f65a 100644 --- a/compiler/optimizing/gvn.h +++ b/compiler/optimizing/gvn.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_GVN_H_ #include "nodes.h" +#include "optimization.h" namespace art { @@ -165,11 +166,11 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> { /** * Optimization phase that removes redundant instruction. */ -class GlobalValueNumberer : public ValueObject { +class GlobalValueNumberer : public HOptimization { public: GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph) - : allocator_(allocator), - graph_(graph), + : HOptimization(graph, true, "GVN"), + allocator_(allocator), block_effects_(allocator, graph->GetBlocks().Size()), loop_effects_(allocator, graph->GetBlocks().Size()), sets_(allocator, graph->GetBlocks().Size()), @@ -186,7 +187,7 @@ class GlobalValueNumberer : public ValueObject { } } - void Run(); + void Run() OVERRIDE; private: // Per-block GVN. Will also update the ValueSet of the dominated and @@ -202,7 +203,6 @@ class GlobalValueNumberer : public ValueObject { SideEffects GetBlockEffects(HBasicBlock* block) const; ArenaAllocator* const allocator_; - HGraph* const graph_; // Side effects of individual blocks, that is the union of the side effects // of the instructions in the block. diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 29eabe7e29..3d65e9a0a4 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -18,11 +18,22 @@ namespace art { +class InstructionSimplifierVisitor : public HGraphVisitor { + public: + explicit InstructionSimplifierVisitor(HGraph* graph) : HGraphVisitor(graph) {} + + private: + void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE; + void VisitEqual(HEqual* equal) OVERRIDE; + void VisitArraySet(HArraySet* equal) OVERRIDE; +}; + void InstructionSimplifier::Run() { - VisitInsertionOrder(); + InstructionSimplifierVisitor visitor(graph_); + visitor.VisitInsertionOrder(); } -void InstructionSimplifier::VisitSuspendCheck(HSuspendCheck* check) { +void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) { HBasicBlock* block = check->GetBlock(); // Currently always keep the suspend check at entry. if (block->IsEntryBlock()) return; @@ -38,7 +49,7 @@ void InstructionSimplifier::VisitSuspendCheck(HSuspendCheck* check) { block->RemoveInstruction(check); } -void InstructionSimplifier::VisitEqual(HEqual* equal) { +void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) { HInstruction* input1 = equal->InputAt(0); HInstruction* input2 = equal->InputAt(1); if (input1->GetType() == Primitive::kPrimBoolean && input2->IsIntConstant()) { @@ -55,4 +66,16 @@ void InstructionSimplifier::VisitEqual(HEqual* equal) { } } +void InstructionSimplifierVisitor::VisitArraySet(HArraySet* instruction) { + HInstruction* value = instruction->GetValue(); + if (value->GetType() != Primitive::kPrimNot) return; + + if (value->IsArrayGet()) { + if (value->AsArrayGet()->GetArray() == instruction->GetArray()) { + // If the code is just swapping elements in the array, no need for a type check. + instruction->ClearNeedsTypeCheck(); + } + } +} + } // namespace art diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h index d74b624518..7068c7fc10 100644 --- a/compiler/optimizing/instruction_simplifier.h +++ b/compiler/optimizing/instruction_simplifier.h @@ -18,21 +18,19 @@ #define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_ #include "nodes.h" +#include "optimization.h" namespace art { /** * Implements optimizations specific to each instruction. */ -class InstructionSimplifier : public HGraphVisitor { +class InstructionSimplifier : public HOptimization { public: - explicit InstructionSimplifier(HGraph* graph) : HGraphVisitor(graph) {} + explicit InstructionSimplifier(HGraph* graph) + : HOptimization(graph, true, "instruction_simplifier") {} - void Run(); - - private: - virtual void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE; - virtual void VisitEqual(HEqual* equal) OVERRIDE; + void Run() OVERRIDE; }; } // namespace art diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index d1555d4e11..e1c8e8ed6e 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -391,6 +391,10 @@ class RegisterSet : public ValueObject { return (register_set & (1 << reg)) != 0; } + size_t GetNumberOfRegisters() const { + return __builtin_popcount(core_registers_) + __builtin_popcount(floating_point_registers_); + } + private: uint32_t core_registers_; uint32_t floating_point_registers_; @@ -503,6 +507,10 @@ class LocationSummary : public ArenaObject<kArenaAllocMisc> { return &live_registers_; } + size_t GetNumberOfLiveRegisters() const { + return live_registers_.GetNumberOfRegisters(); + } + bool InputOverlapsWithOutputOrTemp(uint32_t input_index, bool is_environment) const { if (is_environment) return true; if ((input_index == 0) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 5af3cdd2d6..7d52d7d221 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -521,6 +521,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { M(ParallelMove, Instruction) \ M(ParameterValue, Instruction) \ M(Phi, Instruction) \ + M(Rem, BinaryOperation) \ M(Return, Instruction) \ M(ReturnVoid, Instruction) \ M(StaticFieldGet, Instruction) \ @@ -1756,10 +1757,15 @@ class HDiv : public HBinaryOperation { virtual int32_t Evaluate(int32_t x, int32_t y) const { // Our graph structure ensures we never have 0 for `y` during constant folding. DCHECK_NE(y, 0); - // Special case -1 to avoid getting a SIGFPE on x86. + // Special case -1 to avoid getting a SIGFPE on x86(_64). + return (y == -1) ? -x : x / y; + } + + virtual int64_t Evaluate(int64_t x, int64_t y) const { + DCHECK_NE(y, 0); + // Special case -1 to avoid getting a SIGFPE on x86(_64). return (y == -1) ? -x : x / y; } - virtual int64_t Evaluate(int64_t x, int64_t y) const { return x / y; } uint32_t GetDexPc() const { return dex_pc_; } @@ -1771,6 +1777,33 @@ class HDiv : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HDiv); }; +class HRem : public HBinaryOperation { + public: + HRem(Primitive::Type result_type, HInstruction* left, HInstruction* right, uint32_t dex_pc) + : HBinaryOperation(result_type, left, right), dex_pc_(dex_pc) {} + + virtual int32_t Evaluate(int32_t x, int32_t y) const { + DCHECK_NE(y, 0); + // Special case -1 to avoid getting a SIGFPE on x86(_64). + return (y == -1) ? 0 : x % y; + } + + virtual int64_t Evaluate(int64_t x, int64_t y) const { + DCHECK_NE(y, 0); + // Special case -1 to avoid getting a SIGFPE on x86(_64). + return (y == -1) ? 0 : x % y; + } + + uint32_t GetDexPc() const { return dex_pc_; } + + DECLARE_INSTRUCTION(Rem); + + private: + const uint32_t dex_pc_; + + DISALLOW_COPY_AND_ASSIGN(HRem); +}; + class HDivZeroCheck : public HExpression<1> { public: HDivZeroCheck(HInstruction* value, uint32_t dex_pc) @@ -2034,6 +2067,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } + HInstruction* GetValue() const { return InputAt(1); } + DECLARE_INSTRUCTION(InstanceFieldSet); private: @@ -2050,13 +2085,16 @@ class HArrayGet : public HExpression<2> { SetRawInputAt(1, index); } - virtual bool CanBeMoved() const { return true; } - virtual bool InstructionDataEquals(HInstruction* other) const { + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { UNUSED(other); return true; } void SetType(Primitive::Type type) { type_ = type; } + HInstruction* GetArray() const { return InputAt(0); } + HInstruction* GetIndex() const { return InputAt(1); } + DECLARE_INSTRUCTION(ArrayGet); private: @@ -2072,20 +2110,29 @@ class HArraySet : public HTemplateInstruction<3> { uint32_t dex_pc) : HTemplateInstruction(SideEffects::ChangesSomething()), dex_pc_(dex_pc), - expected_component_type_(expected_component_type) { + expected_component_type_(expected_component_type), + needs_type_check_(value->GetType() == Primitive::kPrimNot) { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); } - virtual bool NeedsEnvironment() const { + bool NeedsEnvironment() const { // We currently always call a runtime method to catch array store // exceptions. - return InputAt(2)->GetType() == Primitive::kPrimNot; + return needs_type_check_; } + void ClearNeedsTypeCheck() { + needs_type_check_ = false; + } + + bool NeedsTypeCheck() const { return needs_type_check_; } + uint32_t GetDexPc() const { return dex_pc_; } + HInstruction* GetArray() const { return InputAt(0); } + HInstruction* GetIndex() const { return InputAt(1); } HInstruction* GetValue() const { return InputAt(2); } Primitive::Type GetComponentType() const { @@ -2104,6 +2151,7 @@ class HArraySet : public HTemplateInstruction<3> { private: const uint32_t dex_pc_; const Primitive::Type expected_component_type_; + bool needs_type_check_; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -2372,6 +2420,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> { MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } + HInstruction* GetValue() const { return InputAt(1); } + DECLARE_INSTRUCTION(StaticFieldSet); private: diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index ea98186d11..d1178d5798 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -21,12 +21,6 @@ namespace art { -void HOptimization::Execute() { - Run(); - visualizer_.DumpGraph(pass_name_); - Check(); -} - void HOptimization::Check() { if (kIsDebugBuild) { if (is_in_ssa_form_) { diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index 59683e2075..d281248f4a 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -29,25 +29,19 @@ class HOptimization : public ValueObject { public: HOptimization(HGraph* graph, bool is_in_ssa_form, - const char* pass_name, - const HGraphVisualizer& visualizer) + const char* pass_name) : graph_(graph), is_in_ssa_form_(is_in_ssa_form), - pass_name_(pass_name), - visualizer_(visualizer) {} + pass_name_(pass_name) {} virtual ~HOptimization() {} - // Execute the optimization pass. - void Execute(); - // Return the name of the pass. const char* GetPassName() const { return pass_name_; } // Peform the analysis itself. virtual void Run() = 0; - private: // Verify the graph; abort if it is not valid. void Check(); @@ -59,9 +53,6 @@ class HOptimization : public ValueObject { const bool is_in_ssa_form_; // Optimization pass name. const char* pass_name_; - // A graph visualiser invoked after the execution of the optimization - // pass if enabled. - const HGraphVisualizer& visualizer_; DISALLOW_COPY_AND_ASSIGN(HOptimization); }; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 6e3653a359..42ac77d1d8 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -26,9 +26,12 @@ #include "dead_code_elimination.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" +#include "elf_writer_quick.h" #include "graph_visualizer.h" #include "gvn.h" #include "instruction_simplifier.h" +#include "jni/quick/jni_compiler.h" +#include "mirror/art_method-inl.h" #include "nodes.h" #include "prepare_for_register_allocation.h" #include "register_allocator.h" @@ -88,15 +91,6 @@ class OptimizingCompiler FINAL : public Compiler { jobject class_loader, const DexFile& dex_file) const OVERRIDE; - CompiledMethod* TryCompile(const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - jobject class_loader, - const DexFile& dex_file) const; - - // For the following methods we will use the fallback. This is a delegation pattern. CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file) const OVERRIDE; @@ -110,13 +104,16 @@ class OptimizingCompiler FINAL : public Compiler { const std::string& android_root, bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Backend* GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const OVERRIDE; + Backend* GetCodeGenerator(CompilationUnit* cu ATTRIBUTE_UNUSED, + void* compilation_unit ATTRIBUTE_UNUSED) const OVERRIDE { + return nullptr; + } - void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE; + void InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const OVERRIDE {} - void Init() const OVERRIDE; + void Init() const OVERRIDE {} - void UnInit() const OVERRIDE; + void UnInit() const OVERRIDE {} private: // Whether we should run any optimization or register allocation. If false, will @@ -128,10 +125,6 @@ class OptimizingCompiler FINAL : public Compiler { std::unique_ptr<std::ostream> visualizer_output_; - // Delegate to another compiler in case the optimizing compiler cannot compile a method. - // Currently the fallback is the quick compiler. - std::unique_ptr<Compiler> delegate_; - DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler); }; @@ -143,21 +136,12 @@ OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver) driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime), total_compiled_methods_(0), unoptimized_compiled_methods_(0), - optimized_compiled_methods_(0), - delegate_(Create(driver, Compiler::Kind::kQuick)) { + optimized_compiled_methods_(0) { if (kIsVisualizerEnabled) { visualizer_output_.reset(new std::ofstream("art.cfg")); } } -void OptimizingCompiler::Init() const { - delegate_->Init(); -} - -void OptimizingCompiler::UnInit() const { - delegate_->UnInit(); -} - OptimizingCompiler::~OptimizingCompiler() { if (total_compiled_methods_ == 0) { LOG(INFO) << "Did not compile any method."; @@ -170,33 +154,28 @@ OptimizingCompiler::~OptimizingCompiler() { } } -bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, - CompilationUnit* cu) const { - return delegate_->CanCompileMethod(method_idx, dex_file, cu); +bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED, + const DexFile& dex_file ATTRIBUTE_UNUSED, + CompilationUnit* cu ATTRIBUTE_UNUSED) const { + return true; } CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file) const { - return delegate_->JniCompile(access_flags, method_idx, dex_file); + return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file); } uintptr_t OptimizingCompiler::GetEntryPointOf(mirror::ArtMethod* method) const { - return delegate_->GetEntryPointOf(method); + return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize( + InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet()))); } bool OptimizingCompiler::WriteElf(art::File* file, OatWriter* oat_writer, const std::vector<const art::DexFile*>& dex_files, const std::string& android_root, bool is_host) const { - return delegate_->WriteElf(file, oat_writer, dex_files, android_root, is_host); -} - -Backend* OptimizingCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_unit) const { - return delegate_->GetCodeGenerator(cu, compilation_unit); -} - -void OptimizingCompiler::InitCompilationUnit(CompilationUnit& cu) const { - delegate_->InitCompilationUnit(cu); + return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host, + *GetCompilerDriver()); } static bool IsInstructionSetSupported(InstructionSet instruction_set) { @@ -211,13 +190,32 @@ static bool CanOptimize(const DexFile::CodeItem& code_item) { return code_item.tries_size_ == 0; } -CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - jobject class_loader, - const DexFile& dex_file) const { +static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) { + HDeadCodeElimination opt1(graph); + HConstantFolding opt2(graph); + SsaRedundantPhiElimination opt3(graph); + SsaDeadPhiElimination opt4(graph); + InstructionSimplifier opt5(graph); + GlobalValueNumberer opt6(graph->GetArena(), graph); + InstructionSimplifier opt7(graph); + + HOptimization* optimizations[] = { &opt1, &opt2, &opt3, &opt4, &opt5, &opt6, &opt7 }; + + for (size_t i = 0; i < arraysize(optimizations); ++i) { + HOptimization* optimization = optimizations[i]; + optimization->Run(); + optimization->Check(); + visualizer.DumpGraph(optimization->GetPassName()); + } +} + +CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, + uint32_t access_flags, + InvokeType invoke_type, + uint16_t class_def_idx, + uint32_t method_idx, + jobject class_loader, + const DexFile& dex_file) const { UNUSED(invoke_type); total_compiled_methods_++; InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet(); @@ -278,16 +276,9 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite visualizer.DumpGraph("ssa"); graph->FindNaturalLoops(); - HDeadCodeElimination(graph, visualizer).Execute(); - HConstantFolding(graph, visualizer).Execute(); + RunOptimizations(graph, visualizer); - SsaRedundantPhiElimination(graph).Run(); - SsaDeadPhiElimination(graph).Run(); - InstructionSimplifier(graph).Run(); - GlobalValueNumberer(graph->GetArena(), graph).Run(); - visualizer.DumpGraph(kGVNPassName); PrepareForRegisterAllocation(graph).Run(); - SsaLivenessAnalysis liveness(*graph, codegen); liveness.Analyze(); visualizer.DumpGraph(kLivenessPassName); @@ -360,23 +351,6 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite } } -CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - jobject class_loader, - const DexFile& dex_file) const { - CompiledMethod* method = TryCompile(code_item, access_flags, invoke_type, class_def_idx, - method_idx, class_loader, dex_file); - if (method != nullptr) { - return method; - } - - return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, - class_loader, dex_file); -} - Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { return new OptimizingCompiler(driver); } diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 4d6e66413d..2948496e15 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -215,9 +215,16 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { // By adding the following interval in the algorithm, we can compute this // maximum before updating locations. LiveInterval* interval = LiveInterval::MakeSlowPathInterval(allocator_, instruction); - interval->AddRange(position, position + 1); - unhandled_core_intervals_.Add(interval); - unhandled_fp_intervals_.Add(interval); + // The start of the interval must be after the position of the safepoint, so that + // we can just check the number of active registers at that position. Note that this + // will include the current interval in the computation of + // `maximum_number_of_live_registers`, so we need a better strategy if this becomes + // a problem. + // TODO: We could put the logic in AddSorted, to ensure the safepoint range is + // after all other intervals starting at that same position. + interval->AddRange(position + 1, position + 2); + AddSorted(&unhandled_core_intervals_, interval); + AddSorted(&unhandled_fp_intervals_, interval); } } @@ -250,6 +257,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { : unhandled_fp_intervals_; DCHECK(unhandled.IsEmpty() || current->StartsBeforeOrAt(unhandled.Peek())); + // Some instructions define their output in fixed register/stack slot. We need // to ensure we know these locations before doing register allocation. For a // given register, we create an interval that covers these locations. The register @@ -475,6 +483,17 @@ void RegisterAllocator::LinearScan() { LiveInterval* current = unhandled_->Pop(); DCHECK(!current->IsFixed() && !current->HasSpillSlot()); DCHECK(unhandled_->IsEmpty() || unhandled_->Peek()->GetStart() >= current->GetStart()); + + if (current->IsSlowPathSafepoint()) { + // Synthesized interval to record the maximum number of live registers + // at safepoints. No need to allocate a register for it. + // We know that current actives are all live at the safepoint (modulo + // the one created by the safepoint). + maximum_number_of_live_registers_ = + std::max(maximum_number_of_live_registers_, active_.Size()); + continue; + } + size_t position = current->GetStart(); // Remember the inactive_ size here since the ones moved to inactive_ from @@ -515,14 +534,6 @@ void RegisterAllocator::LinearScan() { } } - if (current->IsSlowPathSafepoint()) { - // Synthesized interval to record the maximum number of live registers - // at safepoints. No need to allocate a register for it. - maximum_number_of_live_registers_ = - std::max(maximum_number_of_live_registers_, active_.Size()); - continue; - } - // (4) Try to find an available register. bool success = TryAllocateFreeReg(current); @@ -1062,6 +1073,7 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { switch (source.GetKind()) { case Location::kRegister: { locations->AddLiveRegister(source); + DCHECK_LE(locations->GetNumberOfLiveRegisters(), maximum_number_of_live_registers_); if (current->GetType() == Primitive::kPrimNot) { locations->SetRegisterBit(source.reg()); } diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index fec40f93c7..b2cc11996e 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -183,8 +183,7 @@ static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) { static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) { // We place the floating point phi next to this phi. HInstruction* next = phi->GetNext(); - if (next == nullptr - || (next->GetType() != Primitive::kPrimDouble && next->GetType() != Primitive::kPrimFloat)) { + if (next == nullptr || (next->AsPhi()->GetRegNumber() != phi->GetRegNumber())) { ArenaAllocator* allocator = phi->GetBlock()->GetGraph()->GetArena(); HPhi* new_phi = new (allocator) HPhi(allocator, phi->GetRegNumber(), phi->InputCount(), type); for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { @@ -195,9 +194,7 @@ static HPhi* GetFloatOrDoubleEquivalentOfPhi(HPhi* phi, Primitive::Type type) { phi->GetBlock()->InsertPhiAfter(new_phi, phi); return new_phi; } else { - // If there is already a phi with the expected type, we know it is the floating - // point equivalent of this phi. - DCHECK_EQ(next->AsPhi()->GetRegNumber(), phi->GetRegNumber()); + DCHECK_EQ(next->GetType(), type); return next->AsPhi(); } } diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h index 5274f09f3f..b7899712d6 100644 --- a/compiler/optimizing/ssa_phi_elimination.h +++ b/compiler/optimizing/ssa_phi_elimination.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_ #include "nodes.h" +#include "optimization.h" namespace art { @@ -25,15 +26,15 @@ namespace art { * Optimization phase that removes dead phis from the graph. Dead phis are unused * phis, or phis only used by other phis. */ -class SsaDeadPhiElimination : public ValueObject { +class SsaDeadPhiElimination : public HOptimization { public: explicit SsaDeadPhiElimination(HGraph* graph) - : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {} + : HOptimization(graph, true, "dead_phi_elimination"), + worklist_(graph->GetArena(), kDefaultWorklistSize) {} - void Run(); + void Run() OVERRIDE; private: - HGraph* const graph_; GrowableArray<HPhi*> worklist_; static constexpr size_t kDefaultWorklistSize = 8; @@ -47,15 +48,15 @@ class SsaDeadPhiElimination : public ValueObject { * registers might be updated with the same value, or not updated at all. We can just * replace the phi with the value when entering the loop. */ -class SsaRedundantPhiElimination : public ValueObject { +class SsaRedundantPhiElimination : public HOptimization { public: explicit SsaRedundantPhiElimination(HGraph* graph) - : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {} + : HOptimization(graph, true, "redundant_phi_elimination"), + worklist_(graph->GetArena(), kDefaultWorklistSize) {} - void Run(); + void Run() OVERRIDE; private: - HGraph* const graph_; GrowableArray<HPhi*> worklist_; static constexpr size_t kDefaultWorklistSize = 8; diff --git a/compiler/optimizing/ssa_type_propagation.cc b/compiler/optimizing/ssa_type_propagation.cc index 3828142ed2..cb5ce20c46 100644 --- a/compiler/optimizing/ssa_type_propagation.cc +++ b/compiler/optimizing/ssa_type_propagation.cc @@ -90,10 +90,12 @@ void SsaTypePropagation::VisitBasicBlock(HBasicBlock* block) { } } else { for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { - HPhi* phi = it.Current()->AsPhi(); - if (UpdateType(phi)) { - AddDependentInstructionsToWorklist(phi); - } + // Eagerly compute the type of the phi, for quicker convergence. Note + // that we don't need to add users to the worklist because we are + // doing a reverse post-order visit, therefore either the phi users are + // non-loop phi and will be visited later in the visit, or are loop-phis, + // and they are already in the work list. + UpdateType(it.Current()->AsPhi()); } } } diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index 591d461244..9c84bc1e37 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -92,16 +92,29 @@ uint32_t ShifterOperand::encodingArm() const { break; case kRegister: if (is_shift_) { + uint32_t shift_type; + switch (shift_) { + case arm::Shift::ROR: + shift_type = static_cast<uint32_t>(shift_); + CHECK_NE(immed_, 0U); + break; + case arm::Shift::RRX: + shift_type = static_cast<uint32_t>(arm::Shift::ROR); // Same encoding as ROR. + CHECK_EQ(immed_, 0U); + break; + default: + shift_type = static_cast<uint32_t>(shift_); + } // Shifted immediate or register. if (rs_ == kNoRegister) { // Immediate shift. return immed_ << kShiftImmShift | - static_cast<uint32_t>(shift_) << kShiftShift | + shift_type << kShiftShift | static_cast<uint32_t>(rm_); } else { // Register shift. return static_cast<uint32_t>(rs_) << kShiftRegisterShift | - static_cast<uint32_t>(shift_) << kShiftShift | (1 << 4) | + shift_type << kShiftShift | (1 << 4) | static_cast<uint32_t>(rm_); } } else { diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index 39ebf6803c..a1594b02ac 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -1513,10 +1513,8 @@ void Arm32Assembler::MemoryBarrier(ManagedRegister mscratch) { void Arm32Assembler::dmb(DmbOptions flavor) { -#if ANDROID_SMP != 0 int32_t encoding = 0xf57ff05f; // dmb Emit(encoding | flavor); -#endif } diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index 277a9eb0fa..837fe1ec18 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -16,49 +16,208 @@ #include "assembler_arm32.h" +#include <functional> +#include <type_traits> + +#include "base/macros.h" #include "base/stl_util.h" -#include "utils/assembler_test.h" +#include "utils/arm/assembler_arm_test.h" namespace art { -class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler, - arm::Register, arm::SRegister, - uint32_t> { +using std::placeholders::_1; +using std::placeholders::_2; +using std::placeholders::_3; +using std::placeholders::_4; +using std::placeholders::_5; + +// To speed up tests, don't use all register combinations. +static constexpr bool kUseSparseRegisterList = true; + +// To speed up tests, don't use all condition codes. +static constexpr bool kUseSparseConditionList = true; + +// To speed up tests, don't use all shift immediates. +static constexpr bool kUseSparseShiftImmediates = true; + +class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler, + arm::Register, arm::SRegister, + uint32_t, arm::ShifterOperand, arm::Condition> { protected: std::string GetArchitectureString() OVERRIDE { return "arm"; } + std::string GetAssemblerParameters() OVERRIDE { + return " -march=armv7-a -mcpu=cortex-a15"; // Arm-v7a, cortex-a15 (means we have sdiv). + } + + const char* GetAssemblyHeader() OVERRIDE { + return kArm32AssemblyHeader; + } + std::string GetDisassembleParameters() OVERRIDE { return " -D -bbinary -marm --no-show-raw-insn"; } void SetUpHelpers() OVERRIDE { if (registers_.size() == 0) { - registers_.insert(end(registers_), - { // NOLINT(whitespace/braces) - new arm::Register(arm::R0), - new arm::Register(arm::R1), - new arm::Register(arm::R2), - new arm::Register(arm::R3), - new arm::Register(arm::R4), - new arm::Register(arm::R5), - new arm::Register(arm::R6), - new arm::Register(arm::R7), - new arm::Register(arm::R8), - new arm::Register(arm::R9), - new arm::Register(arm::R10), - new arm::Register(arm::R11), - new arm::Register(arm::R12), - new arm::Register(arm::R13), - new arm::Register(arm::R14), - new arm::Register(arm::R15) - }); + if (kUseSparseRegisterList) { + registers_.insert(end(registers_), + { // NOLINT(whitespace/braces) + new arm::Register(arm::R0), + new arm::Register(arm::R1), + new arm::Register(arm::R4), + new arm::Register(arm::R8), + new arm::Register(arm::R11), + new arm::Register(arm::R12), + new arm::Register(arm::R13), + new arm::Register(arm::R14), + new arm::Register(arm::R15) + }); + } else { + registers_.insert(end(registers_), + { // NOLINT(whitespace/braces) + new arm::Register(arm::R0), + new arm::Register(arm::R1), + new arm::Register(arm::R2), + new arm::Register(arm::R3), + new arm::Register(arm::R4), + new arm::Register(arm::R5), + new arm::Register(arm::R6), + new arm::Register(arm::R7), + new arm::Register(arm::R8), + new arm::Register(arm::R9), + new arm::Register(arm::R10), + new arm::Register(arm::R11), + new arm::Register(arm::R12), + new arm::Register(arm::R13), + new arm::Register(arm::R14), + new arm::Register(arm::R15) + }); + } + } + + if (!kUseSparseConditionList) { + conditions_.push_back(arm::Condition::EQ); + conditions_.push_back(arm::Condition::NE); + conditions_.push_back(arm::Condition::CS); + conditions_.push_back(arm::Condition::CC); + conditions_.push_back(arm::Condition::MI); + conditions_.push_back(arm::Condition::PL); + conditions_.push_back(arm::Condition::VS); + conditions_.push_back(arm::Condition::VC); + conditions_.push_back(arm::Condition::HI); + conditions_.push_back(arm::Condition::LS); + conditions_.push_back(arm::Condition::GE); + conditions_.push_back(arm::Condition::LT); + conditions_.push_back(arm::Condition::GT); + conditions_.push_back(arm::Condition::LE); + conditions_.push_back(arm::Condition::AL); + } else { + conditions_.push_back(arm::Condition::EQ); + conditions_.push_back(arm::Condition::NE); + conditions_.push_back(arm::Condition::CC); + conditions_.push_back(arm::Condition::VC); + conditions_.push_back(arm::Condition::HI); + conditions_.push_back(arm::Condition::LT); + conditions_.push_back(arm::Condition::AL); + } + + shifter_operands_.push_back(arm::ShifterOperand(0)); + shifter_operands_.push_back(arm::ShifterOperand(1)); + shifter_operands_.push_back(arm::ShifterOperand(2)); + shifter_operands_.push_back(arm::ShifterOperand(3)); + shifter_operands_.push_back(arm::ShifterOperand(4)); + shifter_operands_.push_back(arm::ShifterOperand(5)); + shifter_operands_.push_back(arm::ShifterOperand(127)); + shifter_operands_.push_back(arm::ShifterOperand(128)); + shifter_operands_.push_back(arm::ShifterOperand(254)); + shifter_operands_.push_back(arm::ShifterOperand(255)); + + if (!kUseSparseRegisterList) { + shifter_operands_.push_back(arm::ShifterOperand(arm::R0)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R1)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R2)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R3)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R4)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R5)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R6)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R7)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R8)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R9)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R10)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R11)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R12)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R13)); + } else { + shifter_operands_.push_back(arm::ShifterOperand(arm::R0)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R1)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R4)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R8)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R11)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R12)); + shifter_operands_.push_back(arm::ShifterOperand(arm::R13)); + } + + std::vector<arm::Shift> shifts { + arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR, arm::Shift::ROR, arm::Shift::RRX + }; + + // ShifterOperands of form "reg shift-type imm." + for (arm::Shift shift : shifts) { + for (arm::Register* reg : registers_) { // Note: this will pick up the sparse set. + if (*reg == arm::R15) { // Skip PC. + continue; + } + if (shift != arm::Shift::RRX) { + if (!kUseSparseShiftImmediates) { + for (uint32_t imm = 1; imm < 32; ++imm) { + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, imm)); + } + } else { + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 1)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 2)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 3)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 7)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 15)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 16)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 30)); + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 31)); + } + } else { + // RRX doesn't have an immediate. + shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 0)); + } + } + } + } + + std::vector<arm::ShifterOperand> CreateRegisterShifts(std::vector<arm::Register*>& base_regs, + int32_t shift_min, int32_t shift_max) { + std::vector<arm::ShifterOperand> res; + static constexpr arm::Shift kShifts[] = { arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR, + arm::Shift::ROR }; + + for (arm::Shift shift : kShifts) { + for (arm::Register* reg : base_regs) { + // Take the min, the max, and three values in between. + res.push_back(arm::ShifterOperand(*reg, shift, shift_min)); + if (shift_min != shift_max) { + res.push_back(arm::ShifterOperand(*reg, shift, shift_max)); + int32_t middle = (shift_min + shift_max) / 2; + res.push_back(arm::ShifterOperand(*reg, shift, middle)); + res.push_back(arm::ShifterOperand(*reg, shift, middle - 1)); + res.push_back(arm::ShifterOperand(*reg, shift, middle + 1)); + } + } } + + return res; } void TearDown() OVERRIDE { - AssemblerTest::TearDown(); + AssemblerArmTest::TearDown(); STLDeleteElements(®isters_); } @@ -70,8 +229,281 @@ class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler, return imm_value; } + std::vector<arm::Condition>& GetConditions() OVERRIDE { + return conditions_; + } + + std::string GetConditionString(arm::Condition c) OVERRIDE { + std::ostringstream oss; + oss << c; + return oss.str(); + } + + arm::Register GetPCRegister() OVERRIDE { + return arm::R15; + } + + std::vector<arm::ShifterOperand>& GetShiftOperands() OVERRIDE { + return shifter_operands_; + } + + std::string GetShiftString(arm::ShifterOperand sop) OVERRIDE { + std::ostringstream oss; + if (sop.IsShift()) { + // Not a rotate... + if (sop.GetShift() == arm::Shift::RRX) { + oss << sop.GetRegister() << ", " << sop.GetShift(); + } else { + oss << sop.GetRegister() << ", " << sop.GetShift() << " #" << sop.GetImmediate(); + } + } else if (sop.IsRegister()) { + oss << sop.GetRegister(); + } else { + CHECK(sop.IsImmediate()); + oss << "#" << sop.GetImmediate(); + } + return oss.str(); + } + + static const char* GetRegTokenFromDepth(int depth) { + switch (depth) { + case 0: + return Base::REG1_TOKEN; + case 1: + return Base::REG2_TOKEN; + case 2: + return REG3_TOKEN; + case 3: + return REG4_TOKEN; + default: + LOG(FATAL) << "Depth problem."; + UNREACHABLE(); + } + } + + void ExecuteAndPrint(std::function<void()> f, std::string fmt, std::ostringstream& oss) { + if (first_) { + first_ = false; + } else { + oss << "\n"; + } + oss << fmt; + + f(); + } + + void TemplateHelper(std::function<void(arm::Register)> f, int depth ATTRIBUTE_UNUSED, + bool without_pc, + std::string fmt, std::ostringstream& oss) { + std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters(); + for (auto reg : registers) { + std::string after_reg = fmt; + + std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg); + size_t reg_index; + const char* reg_token = GetRegTokenFromDepth(depth); + + while ((reg_index = after_reg.find(reg_token)) != std::string::npos) { + after_reg.replace(reg_index, strlen(reg_token), reg_string); + } + + ExecuteAndPrint([&] () { f(*reg); }, after_reg, oss); + } + } + + void TemplateHelper(std::function<void(const arm::ShifterOperand&)> f, int depth ATTRIBUTE_UNUSED, + bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) { + for (const arm::ShifterOperand& shift : GetShiftOperands()) { + std::string after_shift = fmt; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + + ExecuteAndPrint([&] () { f(shift); }, after_shift, oss); + } + } + + void TemplateHelper(std::function<void(arm::Condition)> f, int depth ATTRIBUTE_UNUSED, + bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) { + for (arm::Condition c : GetConditions()) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + ExecuteAndPrint([&] () { f(c); }, after_cond, oss); + } + } + + template <typename... Args> + void TemplateHelper(std::function<void(arm::Register, Args...)> f, int depth, bool without_pc, + std::string fmt, std::ostringstream& oss) { + std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters(); + for (auto reg : registers) { + std::string after_reg = fmt; + + std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg); + size_t reg_index; + const char* reg_token = GetRegTokenFromDepth(depth); + + while ((reg_index = after_reg.find(reg_token)) != std::string::npos) { + after_reg.replace(reg_index, strlen(reg_token), reg_string); + } + + auto lambda = [&] (Args... args) { f(*reg, args...); }; // NOLINT [readability/braces] [4] + TemplateHelper(std::function<void(Args...)>(lambda), depth + 1, without_pc, + after_reg, oss); + } + } + + template <typename... Args> + void TemplateHelper(std::function<void(const arm::ShifterOperand&, Args...)> f, int depth, + bool without_pc, std::string fmt, std::ostringstream& oss) { + for (const arm::ShifterOperand& shift : GetShiftOperands()) { + std::string after_shift = fmt; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + + auto lambda = [&] (Args... args) { f(shift, args...); }; // NOLINT [readability/braces] [4] + TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc, + after_shift, oss); + } + } + + template <typename... Args> + void TemplateHelper(std::function<void(arm::Condition, Args...)> f, int depth, bool without_pc, + std::string fmt, std::ostringstream& oss) { + for (arm::Condition c : GetConditions()) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + auto lambda = [&] (Args... args) { f(c, args...); }; // NOLINT [readability/braces] [4] + TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc, + after_cond, oss); + } + } + + template <typename T1, typename T2> + std::function<void(T1, T2)> GetBoundFunction2(void (arm::Arm32Assembler::*f)(T1, T2)) { + return std::bind(f, GetAssembler(), _1, _2); + } + + template <typename T1, typename T2, typename T3> + std::function<void(T1, T2, T3)> GetBoundFunction3(void (arm::Arm32Assembler::*f)(T1, T2, T3)) { + return std::bind(f, GetAssembler(), _1, _2, _3); + } + + template <typename T1, typename T2, typename T3, typename T4> + std::function<void(T1, T2, T3, T4)> GetBoundFunction4( + void (arm::Arm32Assembler::*f)(T1, T2, T3, T4)) { + return std::bind(f, GetAssembler(), _1, _2, _3, _4); + } + + template <typename T1, typename T2, typename T3, typename T4, typename T5> + std::function<void(T1, T2, T3, T4, T5)> GetBoundFunction5( + void (arm::Arm32Assembler::*f)(T1, T2, T3, T4, T5)) { + return std::bind(f, GetAssembler(), _1, _2, _3, _4, _5); + } + + template <typename... Args> + void GenericTemplateHelper(std::function<void(Args...)> f, bool without_pc, + std::string fmt, std::string test_name) { + first_ = false; + WarnOnCombinations(CountHelper<Args...>(without_pc)); + + std::ostringstream oss; + + TemplateHelper(f, 0, without_pc, fmt, oss); + + oss << "\n"; // Trailing newline. + + DriverStr(oss.str(), test_name); + } + + template <typename... Args> + void T2Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name); + } + + template <typename... Args> + void T3Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name); + } + + template <typename... Args> + void T4Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name); + } + + template <typename... Args> + void T5Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt, + std::string test_name) { + GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name); + } + private: + template <typename T> + size_t CountHelper(bool without_pc) { + size_t tmp; + if (std::is_same<T, arm::Register>::value) { + tmp = GetRegisters().size(); + if (without_pc) { + tmp--;; // Approximation... + } + return tmp; + } else if (std::is_same<T, const arm::ShifterOperand&>::value) { + return GetShiftOperands().size(); + } else if (std::is_same<T, arm::Condition>::value) { + return GetConditions().size(); + } else { + LOG(WARNING) << "Unknown type while counting."; + return 1; + } + } + + template <typename T1, typename T2, typename... Args> + size_t CountHelper(bool without_pc) { + size_t tmp; + if (std::is_same<T1, arm::Register>::value) { + tmp = GetRegisters().size(); + if (without_pc) { + tmp--;; // Approximation... + } + } else if (std::is_same<T1, const arm::ShifterOperand&>::value) { + tmp = GetShiftOperands().size(); + } else if (std::is_same<T1, arm::Condition>::value) { + tmp = GetConditions().size(); + } else { + LOG(WARNING) << "Unknown type while counting."; + tmp = 1; + } + size_t rec = CountHelper<T2, Args...>(without_pc); + return rec * tmp; + } + + bool first_; + + static constexpr const char* kArm32AssemblyHeader = ".arm\n"; + std::vector<arm::Register*> registers_; + std::vector<arm::Condition> conditions_; + std::vector<arm::ShifterOperand> shifter_operands_; }; @@ -79,77 +511,181 @@ TEST_F(AssemblerArm32Test, Toolchain) { EXPECT_TRUE(CheckTools()); } - TEST_F(AssemblerArm32Test, Sbfx) { - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32); + std::vector<std::pair<uint32_t, uint32_t>> immediates; + immediates.push_back({0, 1}); + immediates.push_back({0, 8}); + immediates.push_back({0, 15}); + immediates.push_back({0, 16}); + immediates.push_back({0, 31}); + immediates.push_back({0, 32}); + + immediates.push_back({1, 1}); + immediates.push_back({1, 15}); + immediates.push_back({1, 31}); + + immediates.push_back({8, 1}); + immediates.push_back({8, 15}); + immediates.push_back({8, 16}); + immediates.push_back({8, 24}); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16); - GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24); + immediates.push_back({31, 1}); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8); - GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16); + DriverStr(RepeatRRiiC(&arm::Arm32Assembler::sbfx, immediates, + "sbfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "sbfx"); +} - GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1); +TEST_F(AssemblerArm32Test, Ubfx) { + std::vector<std::pair<uint32_t, uint32_t>> immediates; + immediates.push_back({0, 1}); + immediates.push_back({0, 8}); + immediates.push_back({0, 15}); + immediates.push_back({0, 16}); + immediates.push_back({0, 31}); + immediates.push_back({0, 32}); - const char* expected = - "sbfx r0, r1, #0, #1\n" - "sbfx r0, r1, #0, #8\n" - "sbfx r0, r1, #0, #16\n" - "sbfx r0, r1, #0, #32\n" + immediates.push_back({1, 1}); + immediates.push_back({1, 15}); + immediates.push_back({1, 31}); - "sbfx r0, r1, #8, #1\n" - "sbfx r0, r1, #8, #8\n" - "sbfx r0, r1, #8, #16\n" - "sbfx r0, r1, #8, #24\n" + immediates.push_back({8, 1}); + immediates.push_back({8, 15}); + immediates.push_back({8, 16}); + immediates.push_back({8, 24}); - "sbfx r0, r1, #16, #1\n" - "sbfx r0, r1, #16, #8\n" - "sbfx r0, r1, #16, #16\n" + immediates.push_back({31, 1}); - "sbfx r0, r1, #31, #1\n"; - DriverStr(expected, "sbfx"); + DriverStr(RepeatRRiiC(&arm::Arm32Assembler::ubfx, immediates, + "ubfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "ubfx"); } -TEST_F(AssemblerArm32Test, Ubfx) { - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32); - - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16); - GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24); - - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8); - GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16); - - GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1); - - const char* expected = - "ubfx r0, r1, #0, #1\n" - "ubfx r0, r1, #0, #8\n" - "ubfx r0, r1, #0, #16\n" - "ubfx r0, r1, #0, #32\n" - - "ubfx r0, r1, #8, #1\n" - "ubfx r0, r1, #8, #8\n" - "ubfx r0, r1, #8, #16\n" - "ubfx r0, r1, #8, #24\n" - - "ubfx r0, r1, #16, #1\n" - "ubfx r0, r1, #16, #8\n" - "ubfx r0, r1, #16, #16\n" - - "ubfx r0, r1, #31, #1\n"; - DriverStr(expected, "ubfx"); +TEST_F(AssemblerArm32Test, Mul) { + T4Helper(&arm::Arm32Assembler::mul, true, "mul{cond} {reg1}, {reg2}, {reg3}", "mul"); +} + +TEST_F(AssemblerArm32Test, Mla) { + T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mul"); +} + +/* TODO: Needs support to filter out register combinations, as rdhi must not be equal to rdlo. +TEST_F(AssemblerArm32Test, Umull) { + T5Helper(&arm::Arm32Assembler::umull, true, "umull{cond} {reg1}, {reg2}, {reg3}, {reg4}", + "umull"); +} +*/ + +TEST_F(AssemblerArm32Test, Sdiv) { + T4Helper(&arm::Arm32Assembler::sdiv, true, "sdiv{cond} {reg1}, {reg2}, {reg3}", "sdiv"); +} + +TEST_F(AssemblerArm32Test, Udiv) { + T4Helper(&arm::Arm32Assembler::udiv, true, "udiv{cond} {reg1}, {reg2}, {reg3}", "udiv"); +} + +TEST_F(AssemblerArm32Test, And) { + T4Helper(&arm::Arm32Assembler::and_, true, "and{cond} {reg1}, {reg2}, {shift}", "and"); +} + +TEST_F(AssemblerArm32Test, Eor) { + T4Helper(&arm::Arm32Assembler::eor, true, "eor{cond} {reg1}, {reg2}, {shift}", "eor"); +} + +TEST_F(AssemblerArm32Test, Orr) { + T4Helper(&arm::Arm32Assembler::orr, true, "orr{cond} {reg1}, {reg2}, {shift}", "orr"); +} + +TEST_F(AssemblerArm32Test, Orrs) { + T4Helper(&arm::Arm32Assembler::orrs, true, "orr{cond}s {reg1}, {reg2}, {shift}", "orrs"); +} + +TEST_F(AssemblerArm32Test, Bic) { + T4Helper(&arm::Arm32Assembler::bic, true, "bic{cond} {reg1}, {reg2}, {shift}", "bic"); +} + +TEST_F(AssemblerArm32Test, Mov) { + T3Helper(&arm::Arm32Assembler::mov, true, "mov{cond} {reg1}, {shift}", "mov"); +} + +TEST_F(AssemblerArm32Test, Movs) { + T3Helper(&arm::Arm32Assembler::movs, true, "mov{cond}s {reg1}, {shift}", "movs"); +} + +TEST_F(AssemblerArm32Test, Mvn) { + T3Helper(&arm::Arm32Assembler::mvn, true, "mvn{cond} {reg1}, {shift}", "mvn"); +} + +TEST_F(AssemblerArm32Test, Mvns) { + T3Helper(&arm::Arm32Assembler::mvns, true, "mvn{cond}s {reg1}, {shift}", "mvns"); +} + +TEST_F(AssemblerArm32Test, Add) { + T4Helper(&arm::Arm32Assembler::add, false, "add{cond} {reg1}, {reg2}, {shift}", "add"); +} + +TEST_F(AssemblerArm32Test, Adds) { + T4Helper(&arm::Arm32Assembler::adds, false, "add{cond}s {reg1}, {reg2}, {shift}", "adds"); +} + +TEST_F(AssemblerArm32Test, Adc) { + T4Helper(&arm::Arm32Assembler::adc, false, "adc{cond} {reg1}, {reg2}, {shift}", "adc"); +} + +TEST_F(AssemblerArm32Test, Sub) { + T4Helper(&arm::Arm32Assembler::sub, false, "sub{cond} {reg1}, {reg2}, {shift}", "sub"); +} + +TEST_F(AssemblerArm32Test, Subs) { + T4Helper(&arm::Arm32Assembler::subs, false, "sub{cond}s {reg1}, {reg2}, {shift}", "subs"); +} + +TEST_F(AssemblerArm32Test, Sbc) { + T4Helper(&arm::Arm32Assembler::sbc, false, "sbc{cond} {reg1}, {reg2}, {shift}", "sbc"); +} + +TEST_F(AssemblerArm32Test, Rsb) { + T4Helper(&arm::Arm32Assembler::rsb, true, "rsb{cond} {reg1}, {reg2}, {shift}", "rsb"); +} + +TEST_F(AssemblerArm32Test, Rsbs) { + T4Helper(&arm::Arm32Assembler::rsbs, true, "rsb{cond}s {reg1}, {reg2}, {shift}", "rsbs"); +} + +TEST_F(AssemblerArm32Test, Rsc) { + T4Helper(&arm::Arm32Assembler::rsc, true, "rsc{cond} {reg1}, {reg2}, {shift}", "rsc"); +} + +/* TODO: Needs support to filter out register combinations, as reg1 must not be equal to reg3. +TEST_F(AssemblerArm32Test, Strex) { + RRRCWithoutPCHelper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex"); +} +*/ + +TEST_F(AssemblerArm32Test, Clz) { + T3Helper(&arm::Arm32Assembler::clz, true, "clz{cond} {reg1}, {reg2}", "clz"); +} + +TEST_F(AssemblerArm32Test, Tst) { + T3Helper(&arm::Arm32Assembler::tst, true, "tst{cond} {reg1}, {shift}", "tst"); +} + +TEST_F(AssemblerArm32Test, Teq) { + T3Helper(&arm::Arm32Assembler::teq, true, "teq{cond} {reg1}, {shift}", "teq"); +} + +TEST_F(AssemblerArm32Test, Cmp) { + T3Helper(&arm::Arm32Assembler::cmp, true, "cmp{cond} {reg1}, {shift}", "cmp"); +} + +TEST_F(AssemblerArm32Test, Cmn) { + T3Helper(&arm::Arm32Assembler::cmn, true, "cmn{cond} {reg1}, {shift}", "cmn"); +} + +TEST_F(AssemblerArm32Test, Blx) { + T2Helper(&arm::Arm32Assembler::blx, true, "blx{cond} {reg1}", "blx"); +} + +TEST_F(AssemblerArm32Test, Bx) { + T2Helper(&arm::Arm32Assembler::bx, true, "bx{cond} {reg1}", "bx"); } } // namespace art diff --git a/compiler/utils/arm/assembler_arm_test.h b/compiler/utils/arm/assembler_arm_test.h new file mode 100644 index 0000000000..838abb696d --- /dev/null +++ b/compiler/utils/arm/assembler_arm_test.h @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2014 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_UTILS_ARM_ASSEMBLER_ARM_TEST_H_ +#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_ + +#include "utils/assembler_test.h" + +namespace art { + +template<typename Ass, typename Reg, typename FPReg, typename Imm, typename SOp, typename Cond> +class AssemblerArmTest : public AssemblerTest<Ass, Reg, FPReg, Imm> { + public: + typedef AssemblerTest<Ass, Reg, FPReg, Imm> Base; + + using Base::GetRegisters; + using Base::GetRegName; + using Base::CreateImmediate; + using Base::WarnOnCombinations; + + static constexpr int64_t kFullImmRangeThreshold = 32; + + virtual void FillImmediates(std::vector<Imm>& immediates, int64_t imm_min, int64_t imm_max) { + // Small range: do completely. + if (imm_max - imm_min <= kFullImmRangeThreshold) { + for (int64_t i = imm_min; i <= imm_max; ++i) { + immediates.push_back(CreateImmediate(i)); + } + } else { + immediates.push_back(CreateImmediate(imm_min)); + immediates.push_back(CreateImmediate(imm_max)); + if (imm_min < imm_max - 1) { + immediates.push_back(CreateImmediate(imm_min + 1)); + } + if (imm_min < imm_max - 2) { + immediates.push_back(CreateImmediate(imm_min + 2)); + } + if (imm_min < imm_max - 3) { + immediates.push_back(CreateImmediate(imm_max - 1)); + } + if (imm_min < imm_max - 4) { + immediates.push_back(CreateImmediate((imm_min + imm_max) / 2)); + } + } + } + + std::string RepeatRRIIC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond), + int64_t imm1_min, int64_t imm1_max, + int64_t imm2_min, int64_t imm2_max, + std::string fmt) { + return RepeatTemplatedRRIIC(f, GetRegisters(), GetRegisters(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + imm1_min, imm1_max, imm2_min, imm2_max, + fmt); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRIIC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond), + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + int64_t imm1_min, int64_t imm1_max, + int64_t imm2_min, int64_t imm2_max, + std::string fmt) { + std::vector<Imm> immediates1; + FillImmediates(immediates1, imm1_min, imm1_max); + std::vector<Imm> immediates2; + FillImmediates(immediates2, imm2_min, imm2_max); + + std::vector<Cond>& cond = GetConditions(); + + WarnOnCombinations(cond.size() * immediates1.size() * immediates2.size() * + reg1_registers.size() * reg2_registers.size()); + + std::ostringstream oss; + bool first = true; + for (Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (Imm i : immediates1) { + std::string base = after_cond; + + size_t imm1_index = base.find(IMM1_TOKEN); + if (imm1_index != std::string::npos) { + std::ostringstream sreg; + sreg << i; + std::string imm_string = sreg.str(); + base.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string); + } + + for (Imm j : immediates2) { + std::string base2 = base; + + size_t imm2_index = base2.find(IMM2_TOKEN); + if (imm2_index != std::string::npos) { + std::ostringstream sreg; + sreg << j; + std::string imm_string = sreg.str(); + base2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string); + } + + for (auto reg1 : reg1_registers) { + std::string base3 = base2; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = base3.find(Base::REG1_TOKEN)) != std::string::npos) { + base3.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string base4 = base3; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = base4.find(Base::REG2_TOKEN)) != std::string::npos) { + base4.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << base4; + + (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c); + } + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + std::string RepeatRRiiC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond), + std::vector<std::pair<Imm, Imm>>& immediates, + std::string fmt) { + return RepeatTemplatedRRiiC<Reg, Reg>(f, GetRegisters(), GetRegisters(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + immediates, fmt); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRiiC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond), + const std::vector<Reg1*> reg1_registers, + const std::vector<Reg2*> reg2_registers, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::vector<std::pair<Imm, Imm>>& immediates, + std::string fmt) { + std::vector<Cond>& cond = GetConditions(); + + WarnOnCombinations(cond.size() * immediates.size() * reg1_registers.size() * + reg2_registers.size()); + + std::ostringstream oss; + bool first = true; + for (Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (std::pair<Imm, Imm>& pair : immediates) { + Imm i = pair.first; + Imm j = pair.second; + std::string after_imm1 = after_cond; + + size_t imm1_index = after_imm1.find(IMM1_TOKEN); + if (imm1_index != std::string::npos) { + std::ostringstream sreg; + sreg << i; + std::string imm_string = sreg.str(); + after_imm1.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string); + } + + std::string after_imm2 = after_imm1; + + size_t imm2_index = after_imm2.find(IMM2_TOKEN); + if (imm2_index != std::string::npos) { + std::ostringstream sreg; + sreg << j; + std::string imm_string = sreg.str(); + after_imm2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_imm2; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg2; + + (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c); + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + std::string RepeatRRC(void (Ass::*f)(Reg, Reg, Cond), std::string fmt) { + return RepeatTemplatedRRC(f, GetRegisters(), GetRegisters(), GetConditions(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRC(void (Ass::*f)(Reg1, Reg2, Cond), + const std::vector<Reg1*>& reg1_registers, + const std::vector<Reg2*>& reg2_registers, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::string fmt) { + WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_cond; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg2; + + (Base::GetAssembler()->*f)(*reg1, *reg2, c); + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + std::string RepeatRRRC(void (Ass::*f)(Reg, Reg, Reg, Cond), std::string fmt) { + return RepeatTemplatedRRRC(f, GetRegisters(), GetRegisters(), GetRegisters(), GetConditions(), + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, + fmt); + } + + template <typename Reg1, typename Reg2, typename Reg3> + std::string RepeatTemplatedRRRC(void (Ass::*f)(Reg1, Reg2, Reg3, Cond), + const std::vector<Reg1*>& reg1_registers, + const std::vector<Reg2*>& reg2_registers, + const std::vector<Reg3*>& reg3_registers, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::string (AssemblerArmTest::*GetName3)(const Reg3&), + std::string fmt) { + WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * + reg3_registers.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_cond; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + for (auto reg3 : reg3_registers) { + std::string after_reg3 = after_reg2; + + std::string reg3_string = (this->*GetName3)(*reg3); + size_t reg3_index; + while ((reg3_index = after_reg3.find(REG3_TOKEN)) != std::string::npos) { + after_reg3.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg3; + + (Base::GetAssembler()->*f)(*reg1, *reg2, *reg3, c); + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + template <typename RegT> + std::string RepeatTemplatedRSC(void (Ass::*f)(RegT, SOp, Cond), + const std::vector<RegT*>& registers, + const std::vector<SOp>& shifts, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName)(const RegT&), + std::string fmt) { + WarnOnCombinations(cond.size() * registers.size() * shifts.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (const SOp& shift : shifts) { + std::string after_shift = after_cond; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(Base::SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(Base::SHIFT_TOKEN), shift_string); + } + + for (auto reg : registers) { + std::string after_reg = after_shift; + + std::string reg_string = (this->*GetName)(*reg); + size_t reg_index; + while ((reg_index = after_reg.find(Base::REG_TOKEN)) != std::string::npos) { + after_reg.replace(reg_index, ConstexprStrLen(Base::REG_TOKEN), reg_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg; + + (Base::GetAssembler()->*f)(*reg, shift, c); + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + template <typename Reg1, typename Reg2> + std::string RepeatTemplatedRRSC(void (Ass::*f)(Reg1, Reg2, const SOp&, Cond), + const std::vector<Reg1*>& reg1_registers, + const std::vector<Reg2*>& reg2_registers, + const std::vector<SOp>& shifts, + const std::vector<Cond>& cond, + std::string (AssemblerArmTest::*GetName1)(const Reg1&), + std::string (AssemblerArmTest::*GetName2)(const Reg2&), + std::string fmt) { + WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * shifts.size()); + + std::ostringstream oss; + bool first = true; + for (const Cond& c : cond) { + std::string after_cond = fmt; + + size_t cond_index = after_cond.find(COND_TOKEN); + if (cond_index != std::string::npos) { + after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c)); + } + + for (const SOp& shift : shifts) { + std::string after_shift = after_cond; + + std::string shift_string = GetShiftString(shift); + size_t shift_index; + while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) { + after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string); + } + + for (auto reg1 : reg1_registers) { + std::string after_reg1 = after_shift; + + std::string reg1_string = (this->*GetName1)(*reg1); + size_t reg1_index; + while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) { + after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string); + } + + for (auto reg2 : reg2_registers) { + std::string after_reg2 = after_reg1; + + std::string reg2_string = (this->*GetName2)(*reg2); + size_t reg2_index; + while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) { + after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string); + } + + if (first) { + first = false; + } else { + oss << "\n"; + } + oss << after_reg2; + + (Base::GetAssembler()->*f)(*reg1, *reg2, shift, c); + } + } + } + } + // Add a newline at the end. + oss << "\n"; + + return oss.str(); + } + + protected: + explicit AssemblerArmTest() {} + + virtual std::vector<Cond>& GetConditions() = 0; + virtual std::string GetConditionString(Cond c) = 0; + + virtual std::vector<SOp>& GetShiftOperands() = 0; + virtual std::string GetShiftString(SOp sop) = 0; + + virtual Reg GetPCRegister() = 0; + virtual std::vector<Reg*> GetRegistersWithoutPC() { + std::vector<Reg*> without_pc = GetRegisters(); + Reg pc_reg = GetPCRegister(); + + for (auto it = without_pc.begin(); it != without_pc.end(); ++it) { + if (**it == pc_reg) { + without_pc.erase(it); + break; + } + } + + return without_pc; + } + + static constexpr const char* IMM1_TOKEN = "{imm1}"; + static constexpr const char* IMM2_TOKEN = "{imm2}"; + static constexpr const char* REG3_TOKEN = "{reg3}"; + static constexpr const char* REG4_TOKEN = "{reg4}"; + static constexpr const char* COND_TOKEN = "{cond}"; + static constexpr const char* SHIFT_TOKEN = "{shift}"; + + private: + DISALLOW_COPY_AND_ASSIGN(AssemblerArmTest); +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_ diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 3ab9b2ba03..a34920999e 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -2599,10 +2599,8 @@ void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) { void Thumb2Assembler::dmb(DmbOptions flavor) { -#if ANDROID_SMP != 0 int32_t encoding = 0xf3bf8f50; // dmb in T1 encoding. Emit32(encoding | flavor); -#endif } diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index 02011b87a0..390f2ea449 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -476,9 +476,7 @@ void Arm64Assembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch ATTRIBUTE_UNUSED) { // TODO: Should we check that m_scratch is IP? - see arm. -#if ANDROID_SMP != 0 ___ Dmb(vixl::InnerShareable, vixl::BarrierAll); -#endif } void Arm64Assembler::SignExtend(ManagedRegister mreg, size_t size) { diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 9d3fa01a9d..1fadb916fc 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -38,14 +38,14 @@ constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) { // temp directory. static std::string tmpnam_; +enum class RegisterView { // private + kUsePrimaryName, + kUseSecondaryName +}; + template<typename Ass, typename Reg, typename FPReg, typename Imm> class AssemblerTest : public testing::Test { public: - enum class RegisterView { // private - kUsePrimaryName, - kUseSecondaryName - }; - Ass* GetAssembler() { return assembler_.get(); } @@ -159,6 +159,9 @@ class AssemblerTest : public testing::Test { bool as_uint = false) { std::string str; std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint); + + WarnOnCombinations(imms.size()); + for (int64_t imm : imms) { Imm new_imm = CreateImmediate(imm); (assembler_.get()->*f)(new_imm); @@ -184,12 +187,12 @@ class AssemblerTest : public testing::Test { // This is intended to be run as a test. bool CheckTools() { - if (!FileExists(GetAssemblerCommand())) { + if (!FileExists(FindTool(GetAssemblerCmdName()))) { return false; } LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand(); - if (!FileExists(GetObjdumpCommand())) { + if (!FileExists(FindTool(GetObjdumpCmdName()))) { return false; } LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand(); @@ -197,7 +200,7 @@ class AssemblerTest : public testing::Test { // Disassembly is optional. std::string disassembler = GetDisassembleCommand(); if (disassembler.length() != 0) { - if (!FileExists(disassembler)) { + if (!FileExists(FindTool(GetDisassembleCmdName()))) { return false; } LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand(); @@ -271,7 +274,7 @@ class AssemblerTest : public testing::Test { resolved_assembler_cmd_ = line + GetAssemblerParameters(); - return line; + return resolved_assembler_cmd_; } // Get the name of the objdump, e.g., "objdump" by default. @@ -298,7 +301,7 @@ class AssemblerTest : public testing::Test { resolved_objdump_cmd_ = line + GetObjdumpParameters(); - return line; + return resolved_objdump_cmd_; } // Get the name of the objdump, e.g., "objdump" by default. @@ -324,7 +327,7 @@ class AssemblerTest : public testing::Test { resolved_disassemble_cmd_ = line + GetDisassembleParameters(); - return line; + return resolved_disassemble_cmd_; } // Create a couple of immediate values up to the number of bytes given. @@ -406,6 +409,8 @@ class AssemblerTest : public testing::Test { std::string (AssemblerTest::*GetName1)(const Reg1&), std::string (AssemblerTest::*GetName2)(const Reg2&), std::string fmt) { + WarnOnCombinations(reg1_registers.size() * reg2_registers.size()); + std::string str; for (auto reg1 : reg1_registers) { for (auto reg2 : reg2_registers) { @@ -435,7 +440,6 @@ class AssemblerTest : public testing::Test { return str; } - private: template <RegisterView kRegView> std::string GetRegName(const Reg& reg) { std::ostringstream sreg; @@ -457,12 +461,32 @@ class AssemblerTest : public testing::Test { return sreg.str(); } + // If the assembly file needs a header, return it in a sub-class. + virtual const char* GetAssemblyHeader() { + return nullptr; + } + + void WarnOnCombinations(size_t count) { + if (count > kWarnManyCombinationsThreshold) { + GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow."; + } + } + + static constexpr const char* REG_TOKEN = "{reg}"; + static constexpr const char* REG1_TOKEN = "{reg1}"; + static constexpr const char* REG2_TOKEN = "{reg2}"; + static constexpr const char* IMM_TOKEN = "{imm}"; + + private: template <RegisterView kRegView> std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes, std::string fmt) { const std::vector<Reg*> registers = GetRegisters(); std::string str; std::vector<int64_t> imms = CreateImmediateValues(imm_bytes); + + WarnOnCombinations(registers.size() * imms.size()); + for (auto reg : registers) { for (int64_t imm : imms) { Imm new_imm = CreateImmediate(imm); @@ -547,7 +571,7 @@ class AssemblerTest : public testing::Test { // Compile the assembly file from_file to a binary file to_file. Returns true on success. bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) { - bool have_assembler = FileExists(GetAssemblerCommand()); + bool have_assembler = FileExists(FindTool(GetAssemblerCmdName())); EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand(); if (!have_assembler) { return false; @@ -569,13 +593,20 @@ class AssemblerTest : public testing::Test { args.push_back("-c"); args.push_back(cmd); - return Exec(args, error_msg); + bool success = Exec(args, error_msg); + if (!success) { + LOG(INFO) << "Assembler command line:"; + for (std::string arg : args) { + LOG(INFO) << arg; + } + } + return success; } // Runs objdump -h on the binary file and extracts the first line with .text. // Returns "" on failure. std::string Objdump(std::string file) { - bool have_objdump = FileExists(GetObjdumpCommand()); + bool have_objdump = FileExists(FindTool(GetObjdumpCmdName())); EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand(); if (!have_objdump) { return ""; @@ -652,10 +683,10 @@ class AssemblerTest : public testing::Test { // If you want to take a look at the differences between the ART assembler and GCC, comment // out the removal code. - std::remove(data_name.c_str()); - std::remove(as_name.c_str()); - std::remove((data_name + ".dis").c_str()); - std::remove((as_name + ".dis").c_str()); +// std::remove(data_name.c_str()); +// std::remove(as_name.c_str()); +// std::remove((data_name + ".dis").c_str()); +// std::remove((as_name + ".dis").c_str()); return result; } @@ -714,6 +745,10 @@ class AssemblerTest : public testing::Test { // TODO: Lots of error checking. std::ofstream s_out(res->base_name + ".S"); + const char* header = GetAssemblyHeader(); + if (header != nullptr) { + s_out << header; + } s_out << assembly_code; s_out.close(); @@ -862,13 +897,9 @@ class AssemblerTest : public testing::Test { return tmpnam_; } + static constexpr size_t kWarnManyCombinationsThreshold = 500; static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6; - static constexpr const char* REG_TOKEN = "{reg}"; - static constexpr const char* REG1_TOKEN = "{reg1}"; - static constexpr const char* REG2_TOKEN = "{reg2}"; - static constexpr const char* IMM_TOKEN = "{imm}"; - std::unique_ptr<Ass> assembler_; std::string resolved_assembler_cmd_; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 8ebb40e338..afa4a3b958 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1830,9 +1830,7 @@ void X86Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, O } void X86Assembler::MemoryBarrier(ManagedRegister) { -#if ANDROID_SMP != 0 mfence(); -#endif } void X86Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg, diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 2bb2ed8c9c..8c428f455e 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2371,9 +2371,7 @@ void X86_64Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src } void X86_64Assembler::MemoryBarrier(ManagedRegister) { -#if ANDROID_SMP != 0 mfence(); -#endif } void X86_64Assembler::CreateHandleScopeEntry(ManagedRegister mout_reg, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 2d2a82e985..7d4b726292 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -942,9 +942,11 @@ class Dex2Oat FINAL { oat_location_ = oat_filename_; } } else { - oat_file_.reset(new File(oat_fd_, oat_location_)); + oat_file_.reset(new File(oat_fd_, oat_location_, true)); oat_file_->DisableAutoClose(); - oat_file_->SetLength(0); + if (oat_file_->SetLength(0) != 0) { + PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed."; + } } if (oat_file_.get() == nullptr) { PLOG(ERROR) << "Failed to create oat file: " << oat_location_; @@ -952,6 +954,7 @@ class Dex2Oat FINAL { } if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) { PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_; + oat_file_->Erase(); return false; } return true; @@ -1075,7 +1078,10 @@ class Dex2Oat FINAL { << ". Try: adb shell chmod 777 /data/local/tmp"; continue; } - tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()); + // This is just dumping files for debugging. Ignore errors, and leave remnants. + UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size())); + UNUSED(tmp_file->Flush()); + UNUSED(tmp_file->Close()); LOG(INFO) << "Wrote input to " << tmp_file_name; } } @@ -1214,6 +1220,8 @@ class Dex2Oat FINAL { // Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the // ImageWriter, if necessary. + // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure + // case (when the file will be explicitly erased). bool CreateOatFile() { CHECK(key_value_store_.get() != nullptr); @@ -1266,15 +1274,7 @@ class Dex2Oat FINAL { if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(), oat_file_.get())) { LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath(); - return false; - } - } - - // Flush result to disk. - { - TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_); - if (oat_file_->Flush() != 0) { - LOG(ERROR) << "Failed to flush ELF file " << oat_file_->GetPath(); + oat_file_->Erase(); return false; } } @@ -1295,14 +1295,19 @@ class Dex2Oat FINAL { return true; } - // Strip the oat file, if requested. This first creates a copy from unstripped to stripped, and - // then runs the ElfStripper. Currently only relevant for the portable compiler. - bool Strip() { + // Create a copy from unstripped to stripped. + bool CopyUnstrippedToStripped() { // If we don't want to strip in place, copy from unstripped location to stripped location. // We need to strip after image creation because FixupElf needs to use .strtab. if (oat_unstripped_ != oat_stripped_) { + // If the oat file is still open, flush it. + if (oat_file_.get() != nullptr && oat_file_->IsOpened()) { + if (!FlushCloseOatFile()) { + return false; + } + } + TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_); - oat_file_.reset(); std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str())); std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str())); size_t buffer_size = 8192; @@ -1315,14 +1320,27 @@ class Dex2Oat FINAL { bool write_ok = out->WriteFully(buffer.get(), bytes_read); CHECK(write_ok); } - oat_file_.reset(out.release()); + if (kUsePortableCompiler) { + oat_file_.reset(out.release()); + } else { + if (out->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_; + return false; + } + } VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_; } + return true; + } + // Run the ElfStripper. Currently only relevant for the portable compiler. + bool Strip() { if (kUsePortableCompiler) { // Portable includes debug symbols unconditionally. If we are not supposed to create them, // strip them now. Quick generates debug symbols only when the flag(s) are set. if (!compiler_options_->GetIncludeDebugSymbols()) { + CHECK(oat_file_.get() != nullptr && oat_file_->IsOpened()); + TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_); // Strip unneeded sections for target off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET); @@ -1330,6 +1348,11 @@ class Dex2Oat FINAL { std::string error_msg; if (!ElfFile::Strip(oat_file_.get(), &error_msg)) { LOG(ERROR) << "Failed to strip elf file: " << error_msg; + oat_file_->Erase(); + return false; + } + + if (!FlushCloseOatFile()) { return false; } @@ -1343,6 +1366,31 @@ class Dex2Oat FINAL { return true; } + bool FlushOatFile() { + if (oat_file_.get() != nullptr) { + TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_); + if (oat_file_->Flush() != 0) { + PLOG(ERROR) << "Failed to flush oat file: " << oat_location_ << " / " + << oat_filename_; + oat_file_->Erase(); + return false; + } + } + return true; + } + + bool FlushCloseOatFile() { + if (oat_file_.get() != nullptr) { + std::unique_ptr<File> tmp(oat_file_.release()); + if (tmp->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_ << " / " + << oat_filename_; + return false; + } + } + return true; + } + void DumpTiming() { if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable<TimingLogger>(*timings_); @@ -1356,6 +1404,10 @@ class Dex2Oat FINAL { return compiler_options_.get(); } + bool IsImage() const { + return image_; + } + bool IsHost() const { return is_host_; } @@ -1451,18 +1503,24 @@ class Dex2Oat FINAL { // Destroy ImageWriter before doing FixupElf. image_writer_.reset(); - std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str())); - if (oat_file.get() == nullptr) { - PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_; - return false; - } - // Do not fix up the ELF file if we are --compile-pic if (!compiler_options_->GetCompilePic()) { + std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str())); + if (oat_file.get() == nullptr) { + PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_; + return false; + } + if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) { + oat_file->Erase(); LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath(); return false; } + + if (oat_file->FlushCloseOrErase()) { + PLOG(ERROR) << "Failed to flush and close fixed ELF file " << oat_file->GetPath(); + return false; + } } return true; @@ -1609,49 +1667,121 @@ static void b13564922() { #endif } -static int dex2oat(int argc, char** argv) { - b13564922(); +static int CompileImage(Dex2Oat& dex2oat) { + dex2oat.Compile(); - TimingLogger timings("compiler", false, false); + // Create the boot.oat. + if (!dex2oat.CreateOatFile()) { + return EXIT_FAILURE; + } - Dex2Oat dex2oat(&timings); + // Flush and close the boot.oat. We always expect the output file by name, and it will be + // re-opened from the unstripped name. + if (!dex2oat.FlushCloseOatFile()) { + return EXIT_FAILURE; + } - // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. - dex2oat.ParseArgs(argc, argv); + // Creates the boot.art and patches the boot.oat. + if (!dex2oat.HandleImage()) { + return EXIT_FAILURE; + } - // Check early that the result of compilation can be written - if (!dex2oat.OpenFile()) { + // When given --host, finish early without stripping. + if (dex2oat.IsHost()) { + dex2oat.DumpTiming(); + return EXIT_SUCCESS; + } + + // Copy unstripped to stripped location, if necessary. + if (!dex2oat.CopyUnstrippedToStripped()) { return EXIT_FAILURE; } - LOG(INFO) << CommandLine(); + // Strip, if necessary. + if (!dex2oat.Strip()) { + return EXIT_FAILURE; + } - if (!dex2oat.Setup()) { + // FlushClose again, as stripping might have re-opened the oat file. + if (!dex2oat.FlushCloseOatFile()) { return EXIT_FAILURE; } + dex2oat.DumpTiming(); + return EXIT_SUCCESS; +} + +static int CompileApp(Dex2Oat& dex2oat) { dex2oat.Compile(); + // Create the app oat. if (!dex2oat.CreateOatFile()) { return EXIT_FAILURE; } - if (!dex2oat.HandleImage()) { + // Do not close the oat file here. We might haven gotten the output file by file descriptor, + // which we would lose. + if (!dex2oat.FlushOatFile()) { return EXIT_FAILURE; } + // When given --host, finish early without stripping. if (dex2oat.IsHost()) { + if (!dex2oat.FlushCloseOatFile()) { + return EXIT_FAILURE; + } + dex2oat.DumpTiming(); return EXIT_SUCCESS; } + // Copy unstripped to stripped location, if necessary. This will implicitly flush & close the + // unstripped version. If this is given, we expect to be able to open writable files by name. + if (!dex2oat.CopyUnstrippedToStripped()) { + return EXIT_FAILURE; + } + + // Strip, if necessary. if (!dex2oat.Strip()) { return EXIT_FAILURE; } + // Flush and close the file. + if (!dex2oat.FlushCloseOatFile()) { + return EXIT_FAILURE; + } + dex2oat.DumpTiming(); return EXIT_SUCCESS; } + +static int dex2oat(int argc, char** argv) { + b13564922(); + + TimingLogger timings("compiler", false, false); + + Dex2Oat dex2oat(&timings); + + // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. + dex2oat.ParseArgs(argc, argv); + + // Check early that the result of compilation can be written + if (!dex2oat.OpenFile()) { + return EXIT_FAILURE; + } + + LOG(INFO) << CommandLine(); + + if (!dex2oat.Setup()) { + return EXIT_FAILURE; + } + + if (dex2oat.IsImage()) { + return CompileImage(dex2oat); + } else { + return CompileApp(dex2oat); + } +} } // namespace art int main(int argc, char** argv) { diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h index 9cd631c984..966ee3ac89 100644 --- a/disassembler/disassembler.h +++ b/disassembler/disassembler.h @@ -34,8 +34,14 @@ class DisassemblerOptions { // Base addess for calculating relative code offsets when absolute_addresses_ is false. const uint8_t* const base_address_; - DisassemblerOptions(bool absolute_addresses, const uint8_t* base_address) - : absolute_addresses_(absolute_addresses), base_address_(base_address) {} + // If set, the disassembler is allowed to look at load targets in literal + // pools. + const bool can_read_literals_; + + DisassemblerOptions(bool absolute_addresses, const uint8_t* base_address, + bool can_read_literals) + : absolute_addresses_(absolute_addresses), base_address_(base_address), + can_read_literals_(can_read_literals) {} private: DISALLOW_COPY_AND_ASSIGN(DisassemblerOptions); diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc index 229ac9755f..fe50421ed4 100644 --- a/disassembler/disassembler_arm64.cc +++ b/disassembler/disassembler_arm64.cc @@ -27,10 +27,88 @@ namespace art { namespace arm64 { +void CustomDisassembler::AppendRegisterNameToOutput( + const vixl::Instruction* instr, + const vixl::CPURegister& reg) { + USE(instr); + if (reg.IsRegister()) { + // This enumeration should mirror the declarations in + // runtime/arch/arm64/registers_arm64.h. We do not include that file to + // avoid a dependency on libart. + enum { + TR = 18, + ETR = 21, + IP0 = 16, + IP1 = 17, + FP = 29, + LR = 30 + }; + switch (reg.code()) { + case IP0: AppendToOutput(reg.Is64Bits() ? "ip0" : "wip0"); return; + case IP1: AppendToOutput(reg.Is64Bits() ? "ip1" : "wip1"); return; + case TR: AppendToOutput(reg.Is64Bits() ? "tr" : "w18"); return; + case ETR: AppendToOutput(reg.Is64Bits() ? "etr" : "w21"); return; + case FP: AppendToOutput(reg.Is64Bits() ? "fp" : "w29"); return; + case LR: AppendToOutput(reg.Is64Bits() ? "lr" : "w30"); return; + default: + // Fall through. + break; + } + } + // Print other register names as usual. + Disassembler::AppendRegisterNameToOutput(instr, reg); +} + +void CustomDisassembler::VisitLoadLiteral(const vixl::Instruction* instr) { + Disassembler::VisitLoadLiteral(instr); + + if (!read_literals_) { + return; + } + + char* buffer = buffer_; + char* buffer_end = buffer_ + buffer_size_; + + // Find the end position in the buffer. + while ((*buffer != 0) && (buffer < buffer_end)) { + ++buffer; + } + + void* data_address = instr->LiteralAddress(); + ptrdiff_t buf_size_remaining = buffer_end - buffer; + vixl::Instr op = instr->Mask(vixl::LoadLiteralMask); + + switch (op) { + case vixl::LDR_w_lit: + case vixl::LDR_x_lit: + case vixl::LDRSW_x_lit: { + int64_t data = op == vixl::LDR_x_lit ? *reinterpret_cast<int64_t*>(data_address) + : *reinterpret_cast<int32_t*>(data_address); + snprintf(buffer, buf_size_remaining, " (0x%" PRIx64 " / %" PRId64 ")", data, data); + break; + } + case vixl::LDR_s_lit: + case vixl::LDR_d_lit: { + double data = (op == vixl::LDR_s_lit) ? *reinterpret_cast<float*>(data_address) + : *reinterpret_cast<double*>(data_address); + snprintf(buffer, buf_size_remaining, " (%g)", data); + break; + } + default: + break; + } +} + size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) { const vixl::Instruction* instr = reinterpret_cast<const vixl::Instruction*>(begin); decoder.Decode(instr); - os << FormatInstructionPointer(begin) + // TODO: Use FormatInstructionPointer() once VIXL provides the appropriate + // features. + // VIXL does not yet allow remapping addresses disassembled. Using + // FormatInstructionPointer() would show incoherences between the instruction + // location addresses and the target addresses disassembled by VIXL (eg. for + // branch instructions). + os << StringPrintf("%p", instr) << StringPrintf(": %08x\t%s\n", instr->InstructionBits(), disasm.GetOutput()); return vixl::kInstructionSize; } diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h index e56fe4f08a..a370b8ded9 100644 --- a/disassembler/disassembler_arm64.h +++ b/disassembler/disassembler_arm64.h @@ -28,9 +28,35 @@ namespace art { namespace arm64 { +class CustomDisassembler FINAL : public vixl::Disassembler { + public: + explicit CustomDisassembler(bool read_literals) : + vixl::Disassembler(), read_literals_(read_literals) {} + + // Use register aliases in the disassembly. + virtual void AppendRegisterNameToOutput(const vixl::Instruction* instr, + const vixl::CPURegister& reg) OVERRIDE; + + // Improve the disassembly of literal load instructions. + virtual void VisitLoadLiteral(const vixl::Instruction* instr) OVERRIDE; + + private: + // Indicate if the disassembler should read data loaded from literal pools. + // This should only be enabled if reading the target of literal loads is safe. + // Here are possible outputs when the option is on or off: + // read_literals_ | disassembly + // true | 0x72681558: 1c000acb ldr s11, pc+344 (addr 0x726816b0) + // false | 0x72681558: 1c000acb ldr s11, pc+344 (addr 0x726816b0) (3.40282e+38) + const bool read_literals_; +}; + class DisassemblerArm64 FINAL : public Disassembler { public: - explicit DisassemblerArm64(DisassemblerOptions* options) : Disassembler(options) { + // TODO: Update this code once VIXL provides the ability to map code addresses + // to disassemble as a different address (the way FormatInstructionPointer() + // does). + explicit DisassemblerArm64(DisassemblerOptions* options) : + Disassembler(options), disasm(options->can_read_literals_) { decoder.AppendVisitor(&disasm); } @@ -39,7 +65,7 @@ class DisassemblerArm64 FINAL : public Disassembler { private: vixl::Decoder decoder; - vixl::Disassembler disasm; + CustomDisassembler disasm; DISALLOW_COPY_AND_ASSIGN(DisassemblerArm64); }; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index d6309f7be3..08352de7e6 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -182,8 +182,8 @@ class OatSymbolizer FINAL : public CodeOutput { bool result = builder_->Write(); - elf_output_->Flush(); - elf_output_->Close(); + // Ignore I/O errors. + UNUSED(elf_output_->FlushClose()); return result; } @@ -386,9 +386,11 @@ class OatDumper { : oat_file_(oat_file), oat_dex_files_(oat_file.GetOatDexFiles()), options_(options), - disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(), + instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()), + disassembler_(Disassembler::Create(instruction_set_, new DisassemblerOptions(options_->absolute_addresses_, - oat_file.Begin()))) { + oat_file.Begin(), + true /* can_read_litals_ */))) { CHECK(options_->class_loader_ != nullptr); AddAllOffsets(); } @@ -398,6 +400,10 @@ class OatDumper { delete disassembler_; } + InstructionSet GetInstructionSet() { + return instruction_set_; + } + bool Dump(std::ostream& os) { bool success = true; const OatHeader& oat_header = oat_file_.GetOatHeader(); @@ -514,7 +520,7 @@ class OatDumper { return end_offset - begin_offset; } - InstructionSet GetInstructionSet() { + InstructionSet GetOatInstructionSet() { return oat_file_.GetOatHeader().GetInstructionSet(); } @@ -1259,6 +1265,7 @@ class OatDumper { const OatFile& oat_file_; const std::vector<const OatFile::OatDexFile*> oat_dex_files_; const OatDumperOptions* options_; + InstructionSet instruction_set_; std::set<uintptr_t> offsets_; Disassembler* disassembler_; }; @@ -1520,7 +1527,8 @@ class ImageDumper { const void* GetQuickOatCodeBegin(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const void* quick_code = m->GetEntryPointFromQuickCompiledCode(); + const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize( + InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet())); if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) { quick_code = oat_dumper_->GetQuickOatCode(m); } @@ -1621,11 +1629,14 @@ class ImageDumper { } } } else if (obj->IsArtMethod()) { + const size_t image_pointer_size = InstructionSetPointerSize( + state->oat_dumper_->GetOatInstructionSet()); mirror::ArtMethod* method = obj->AsArtMethod(); if (method->IsNative()) { // TODO: portable dumping. - DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method); - DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method); + DCHECK(method->GetNativeGcMapPtrSize(image_pointer_size) == nullptr) + << PrettyMethod(method); + DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method); bool first_occurrence; const void* quick_oat_code = state->GetQuickOatCodeBegin(method); uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method); @@ -1633,33 +1644,36 @@ class ImageDumper { if (first_occurrence) { state->stats_.native_to_managed_code_bytes += quick_oat_code_size; } - if (quick_oat_code != method->GetEntryPointFromQuickCompiledCode()) { + if (quick_oat_code != method->GetEntryPointFromQuickCompiledCodePtrSize( + image_pointer_size)) { indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code); } } else if (method->IsAbstract() || method->IsCalleeSaveMethod() || method->IsResolutionMethod() || method->IsImtConflictMethod() || method->IsImtUnimplementedMethod() || method->IsClassInitializer()) { - DCHECK(method->GetNativeGcMap() == nullptr) << PrettyMethod(method); - DCHECK(method->GetMappingTable() == nullptr) << PrettyMethod(method); + DCHECK(method->GetNativeGcMapPtrSize(image_pointer_size) == nullptr) + << PrettyMethod(method); + DCHECK(method->GetMappingTable(image_pointer_size) == nullptr) << PrettyMethod(method); } else { const DexFile::CodeItem* code_item = method->GetCodeItem(); size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2; state->stats_.dex_instruction_bytes += dex_instruction_bytes; bool first_occurrence; - size_t gc_map_bytes = state->ComputeOatSize(method->GetNativeGcMap(), &first_occurrence); + size_t gc_map_bytes = state->ComputeOatSize( + method->GetNativeGcMapPtrSize(image_pointer_size), &first_occurrence); if (first_occurrence) { state->stats_.gc_map_bytes += gc_map_bytes; } size_t pc_mapping_table_bytes = - state->ComputeOatSize(method->GetMappingTable(), &first_occurrence); + state->ComputeOatSize(method->GetMappingTable(image_pointer_size), &first_occurrence); if (first_occurrence) { state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes; } size_t vmap_table_bytes = - state->ComputeOatSize(method->GetVmapTable(), &first_occurrence); + state->ComputeOatSize(method->GetVmapTable(image_pointer_size), &first_occurrence); if (first_occurrence) { state->stats_.vmap_table_bytes += vmap_table_bytes; } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 6b6d11e96c..b15c7127ee 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -175,7 +175,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta, } gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetImageSpace(); - PatchOat p(image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), + PatchOat p(isa, image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), delta, timings); t.NewTiming("Patching files"); if (!p.PatchImage()) { @@ -297,7 +297,7 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d CHECK(is_oat_pic == NOT_PIC); } - PatchOat p(elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), + PatchOat p(isa, elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), delta, timings); t.NewTiming("Patching files"); if (!skip_patching_oat && !p.PatchElf()) { @@ -532,39 +532,44 @@ void PatchOat::VisitObject(mirror::Object* object) { PatchOat::PatchVisitor visitor(this, copy); object->VisitReferences<true, kVerifyNone>(visitor, visitor); if (object->IsArtMethod<kVerifyNone>()) { - FixupMethod(static_cast<mirror::ArtMethod*>(object), - static_cast<mirror::ArtMethod*>(copy)); + FixupMethod(down_cast<mirror::ArtMethod*>(object), down_cast<mirror::ArtMethod*>(copy)); } } void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) { + const size_t pointer_size = InstructionSetPointerSize(isa_); // Just update the entry points if it looks like we should. // TODO: sanity check all the pointers' values uintptr_t portable = reinterpret_cast<uintptr_t>( - object->GetEntryPointFromPortableCompiledCode<kVerifyNone>()); + object->GetEntryPointFromPortableCompiledCodePtrSize<kVerifyNone>(pointer_size)); if (portable != 0) { - copy->SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(portable + delta_)); + copy->SetEntryPointFromPortableCompiledCodePtrSize(reinterpret_cast<void*>(portable + delta_), + pointer_size); } uintptr_t quick= reinterpret_cast<uintptr_t>( - object->GetEntryPointFromQuickCompiledCode<kVerifyNone>()); + object->GetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>(pointer_size)); if (quick != 0) { - copy->SetEntryPointFromQuickCompiledCode(reinterpret_cast<void*>(quick + delta_)); + copy->SetEntryPointFromQuickCompiledCodePtrSize(reinterpret_cast<void*>(quick + delta_), + pointer_size); } uintptr_t interpreter = reinterpret_cast<uintptr_t>( - object->GetEntryPointFromInterpreter<kVerifyNone>()); + object->GetEntryPointFromInterpreterPtrSize<kVerifyNone>(pointer_size)); if (interpreter != 0) { - copy->SetEntryPointFromInterpreter( - reinterpret_cast<mirror::EntryPointFromInterpreter*>(interpreter + delta_)); + copy->SetEntryPointFromInterpreterPtrSize( + reinterpret_cast<mirror::EntryPointFromInterpreter*>(interpreter + delta_), pointer_size); } - uintptr_t native_method = reinterpret_cast<uintptr_t>(object->GetNativeMethod()); + uintptr_t native_method = reinterpret_cast<uintptr_t>( + object->GetEntryPointFromJniPtrSize(pointer_size)); if (native_method != 0) { - copy->SetNativeMethod(reinterpret_cast<void*>(native_method + delta_)); + copy->SetEntryPointFromJniPtrSize(reinterpret_cast<void*>(native_method + delta_), + pointer_size); } - uintptr_t native_gc_map = reinterpret_cast<uintptr_t>(object->GetNativeGcMap()); + uintptr_t native_gc_map = reinterpret_cast<uintptr_t>( + object->GetNativeGcMapPtrSize(pointer_size)); if (native_gc_map != 0) { - copy->SetNativeGcMap(reinterpret_cast<uint8_t*>(native_gc_map + delta_)); + copy->SetNativeGcMapPtrSize(reinterpret_cast<uint8_t*>(native_gc_map + delta_), pointer_size); } } @@ -899,6 +904,20 @@ static File* CreateOrOpen(const char* name, bool* created) { } } +// Either try to close the file (close=true), or erase it. +static bool FinishFile(File* file, bool close) { + if (close) { + if (file->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close file."; + return false; + } + return true; + } else { + file->Erase(); + return false; + } +} + static int patchoat(int argc, char **argv) { InitLogging(argv); MemMap::Init(); @@ -1170,7 +1189,7 @@ static int patchoat(int argc, char **argv) { if (output_image_filename.empty()) { output_image_filename = "output-image-file"; } - output_image.reset(new File(output_image_fd, output_image_filename)); + output_image.reset(new File(output_image_fd, output_image_filename, true)); } else { CHECK(!output_image_filename.empty()); output_image.reset(CreateOrOpen(output_image_filename.c_str(), &new_image_out)); @@ -1184,7 +1203,7 @@ static int patchoat(int argc, char **argv) { if (input_oat_filename.empty()) { input_oat_filename = "input-oat-file"; } - input_oat.reset(new File(input_oat_fd, input_oat_filename)); + input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); if (input_oat == nullptr) { // Unlikely, but ensure exhaustive logging in non-0 exit code case LOG(ERROR) << "Failed to open input oat file by its FD" << input_oat_fd; @@ -1203,7 +1222,7 @@ static int patchoat(int argc, char **argv) { if (output_oat_filename.empty()) { output_oat_filename = "output-oat-file"; } - output_oat.reset(new File(output_oat_fd, output_oat_filename)); + output_oat.reset(new File(output_oat_fd, output_oat_filename, true)); if (output_oat == nullptr) { // Unlikely, but ensure exhaustive logging in non-0 exit code case LOG(ERROR) << "Failed to open output oat file by its FD" << output_oat_fd; @@ -1276,14 +1295,20 @@ static int patchoat(int argc, char **argv) { output_oat.get(), output_image.get(), isa, &timings, output_oat_fd >= 0, // was it opened from FD? new_oat_out); + // The order here doesn't matter. If the first one is successfully saved and the second one + // erased, ImageSpace will still detect a problem and not use the files. + ret = ret && FinishFile(output_image.get(), ret); + ret = ret && FinishFile(output_oat.get(), ret); } else if (have_oat_files) { TimingLogger::ScopedTiming pt("patch oat", &timings); ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings, output_oat_fd >= 0, // was it opened from FD? new_oat_out); + ret = ret && FinishFile(output_oat.get(), ret); } else if (have_image_files) { TimingLogger::ScopedTiming pt("patch image", &timings); ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings); + ret = ret && FinishFile(output_image.get(), ret); } else { CHECK(false); ret = true; diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 5a3545bb7b..578df3aef7 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -61,15 +61,16 @@ class PatchOat { // Takes ownership only of the ElfFile. All other pointers are only borrowed. PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings) : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta), - timings_(timings) {} - PatchOat(MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, + isa_(kNone), timings_(timings) {} + PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, TimingLogger* timings) : image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), timings_(timings) {} - PatchOat(ElfFile* oat_file, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, - MemMap* heap, off_t delta, TimingLogger* timings) + delta_(delta), isa_(isa), timings_(timings) {} + PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image, + gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, + TimingLogger* timings) : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), timings_(timings) {} + delta_(delta), isa_(isa), timings_(timings) {} ~PatchOat() {} // Was the .art image at image_path made with --compile-pic ? @@ -156,8 +157,10 @@ class PatchOat { const MemMap* const heap_; // The amount we are changing the offset by. const off_t delta_; - // Timing splits. - TimingLogger* const timings_; + // Active instruction set, used to know the entrypoint size. + const InstructionSet isa_; + + TimingLogger* timings_; DISALLOW_IMPLICIT_CONSTRUCTORS(PatchOat); }; diff --git a/runtime/Android.mk b/runtime/Android.mk index 25fe45f5cd..087c0ea990 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -302,6 +302,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \ base/allocator.h \ base/mutex.h \ debugger.h \ + base/unix_file/fd_file.h \ dex_file.h \ dex_instruction.h \ gc/allocator/rosalloc.h \ @@ -389,7 +390,9 @@ define build-libart LOCAL_CPP_EXTENSION := $$(ART_CPP_EXTENSION) ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := libart - LOCAL_FDO_SUPPORT := true + ifeq ($$(art_target_or_host),target) + LOCAL_FDO_SUPPORT := true + endif else # debug LOCAL_MODULE := libartd endif diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index f49c0371e0..f8590d3bd2 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -108,12 +108,7 @@ const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t } const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() { -#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0) - const bool smp = false; -#else const bool smp = true; -#endif - #if defined(__ARM_ARCH_EXT_IDIV__) const bool has_div = true; #else @@ -204,11 +199,8 @@ static void bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED, siginfo_t* si ATT } const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() { -#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0) - const bool smp = false; -#else const bool smp = true; -#endif + // See if have a sdiv instruction. Register a signal handler and try to execute an sdiv // instruction. If we get a SIGILL then it's not supported. struct sigaction sa, osa; diff --git a/runtime/arch/arm/portable_entrypoints_arm.S b/runtime/arch/arm/portable_entrypoints_arm.S index d37e7604eb..89ac1f7a03 100644 --- a/runtime/arch/arm/portable_entrypoints_arm.S +++ b/runtime/arch/arm/portable_entrypoints_arm.S @@ -53,7 +53,7 @@ ENTRY art_portable_invoke_stub mov ip, #0 @ set ip to 0 str ip, [sp] @ store NULL for method* at bottom of frame add sp, #16 @ first 4 args are not passed on stack for portable - ldr ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET] @ get pointer to the code + ldr ip, [r0, #MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32] @ get pointer to the code blx ip @ call the method mov sp, r11 @ restore the stack pointer ldr ip, [sp, #24] @ load the result pointer diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 632b41435f..1782db50b9 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -422,7 +422,7 @@ ENTRY art_quick_invoke_stub_internal mov r4, #SUSPEND_CHECK_INTERVAL @ reset r4 to suspend check interval #endif - ldr ip, [r0, #MIRROR_ART_METHOD_QUICK_CODE_OFFSET] @ get pointer to the code + ldr ip, [r0, #MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32] @ get pointer to the code blx ip @ call the method mov sp, r11 @ restore the stack pointer diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 696dd94e58..a1270dcde9 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -26,15 +26,29 @@ namespace art { const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromVariant( const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED) { - if (variant != "default" && variant != "generic") { - std::ostringstream os; - os << "Unexpected CPU variant for Arm64: " << variant; - *error_msg = os.str(); - return nullptr; - } const bool smp = true; // Conservative default. - const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s. - return new Arm64InstructionSetFeatures(smp, is_a53); + + // Look for variants that need a fix for a53 erratum 835769. + static const char* arm64_variants_with_a53_835769_bug[] = { + "default", "generic" // Pessimistically assume all generic ARM64s are A53s. + }; + bool needs_a53_835769_fix = FindVariantInArray(arm64_variants_with_a53_835769_bug, + arraysize(arm64_variants_with_a53_835769_bug), + variant); + + if (!needs_a53_835769_fix) { + // Check to see if this is an expected variant. + static const char* arm64_known_variants[] = { + "denver64" + }; + if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) { + std::ostringstream os; + os << "Unexpected CPU variant for Arm64: " << variant; + *error_msg = os.str(); + return nullptr; + } + } + return new Arm64InstructionSetFeatures(smp, needs_a53_835769_fix); } const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) { @@ -44,12 +58,7 @@ const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromBitmap(uint3 } const Arm64InstructionSetFeatures* Arm64InstructionSetFeatures::FromCppDefines() { -#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0) - const bool smp = false; -#else const bool smp = true; -#endif - const bool is_a53 = true; // Pessimistically assume all ARM64s are A53s. return new Arm64InstructionSetFeatures(smp, is_a53); } diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h index ee41536449..b0c66b3272 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.h +++ b/runtime/arch/arm64/instruction_set_features_arm64.h @@ -70,8 +70,8 @@ class Arm64InstructionSetFeatures FINAL : public InstructionSetFeatures { std::string* error_msg) const OVERRIDE; private: - explicit Arm64InstructionSetFeatures(bool smp, bool is_a53) - : InstructionSetFeatures(smp), fix_cortex_a53_835769_(is_a53) { + explicit Arm64InstructionSetFeatures(bool smp, bool needs_a53_835769_fix) + : InstructionSetFeatures(smp), fix_cortex_a53_835769_(needs_a53_835769_fix) { } // Bitmap positions for encoding features as a bitmap. diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 147d4348be..44159354ab 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -564,7 +564,7 @@ SAVE_SIZE_AND_METHOD=SAVE_SIZE+STACK_REFERENCE_SIZE .macro INVOKE_STUB_CALL_AND_RETURN // load method-> METHOD_QUICK_CODE_OFFSET - ldr x9, [x0 , #MIRROR_ART_METHOD_QUICK_CODE_OFFSET] + ldr x9, [x0 , #MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64] // Branch to method. blr x9 diff --git a/runtime/arch/instruction_set.h b/runtime/arch/instruction_set.h index 46622eb37d..e413880094 100644 --- a/runtime/arch/instruction_set.h +++ b/runtime/arch/instruction_set.h @@ -127,6 +127,10 @@ static inline bool Is64BitInstructionSet(InstructionSet isa) { } } +static inline size_t InstructionSetPointerSize(InstructionSet isa) { + return Is64BitInstructionSet(isa) ? 8U : 4U; +} + static inline size_t GetBytesPerGprSpillLocation(InstructionSet isa) { switch (isa) { case kArm: diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc index 83571cf2b1..e6f4e7ab01 100644 --- a/runtime/arch/instruction_set_features_test.cc +++ b/runtime/arch/instruction_set_features_test.cc @@ -27,12 +27,17 @@ namespace art { #ifdef HAVE_ANDROID_OS +#if defined(__aarch64__) +TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) { + LOG(WARNING) << "Test disabled due to no CPP define for A53 erratum 835769"; +#else TEST(InstructionSetFeaturesTest, FeaturesFromSystemPropertyVariant) { +#endif // Take the default set of instruction features from the build. std::unique_ptr<const InstructionSetFeatures> instruction_set_features( InstructionSetFeatures::FromCppDefines()); - // Read the features property. + // Read the variant property. std::string key = StringPrintf("dalvik.vm.isa.%s.variant", GetInstructionSetString(kRuntimeISA)); char dex2oat_isa_variant[PROPERTY_VALUE_MAX]; if (property_get(key.c_str(), dex2oat_isa_variant, nullptr) > 0) { @@ -49,29 +54,41 @@ TEST(InstructionSetFeaturesTest, FeaturesFromSystemPropertyVariant) { } } +#if defined(__aarch64__) +TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyString) { + LOG(WARNING) << "Test disabled due to no CPP define for A53 erratum 835769"; +#else TEST(InstructionSetFeaturesTest, FeaturesFromSystemPropertyString) { +#endif // Take the default set of instruction features from the build. std::unique_ptr<const InstructionSetFeatures> instruction_set_features( InstructionSetFeatures::FromCppDefines()); - // Read the features property. - std::string key = StringPrintf("dalvik.vm.isa.%s.features", GetInstructionSetString(kRuntimeISA)); - char dex2oat_isa_features[PROPERTY_VALUE_MAX]; - if (property_get(key.c_str(), dex2oat_isa_features, nullptr) > 0) { - // Use features from property to build InstructionSetFeatures and check against build's - // features. - std::string error_msg; - std::unique_ptr<const InstructionSetFeatures> base_features( - InstructionSetFeatures::FromVariant(kRuntimeISA, "default", &error_msg)); - ASSERT_TRUE(base_features.get() != nullptr) << error_msg; - - std::unique_ptr<const InstructionSetFeatures> property_features( - base_features->AddFeaturesFromString(dex2oat_isa_features, &error_msg)); - ASSERT_TRUE(property_features.get() != nullptr) << error_msg; - - EXPECT_TRUE(property_features->Equals(instruction_set_features.get())) + // Read the variant property. + std::string variant_key = StringPrintf("dalvik.vm.isa.%s.variant", + GetInstructionSetString(kRuntimeISA)); + char dex2oat_isa_variant[PROPERTY_VALUE_MAX]; + if (property_get(variant_key.c_str(), dex2oat_isa_variant, nullptr) > 0) { + // Read the features property. + std::string features_key = StringPrintf("dalvik.vm.isa.%s.features", + GetInstructionSetString(kRuntimeISA)); + char dex2oat_isa_features[PROPERTY_VALUE_MAX]; + if (property_get(features_key.c_str(), dex2oat_isa_features, nullptr) > 0) { + // Use features from property to build InstructionSetFeatures and check against build's + // features. + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> base_features( + InstructionSetFeatures::FromVariant(kRuntimeISA, dex2oat_isa_variant, &error_msg)); + ASSERT_TRUE(base_features.get() != nullptr) << error_msg; + + std::unique_ptr<const InstructionSetFeatures> property_features( + base_features->AddFeaturesFromString(dex2oat_isa_features, &error_msg)); + ASSERT_TRUE(property_features.get() != nullptr) << error_msg; + + EXPECT_TRUE(property_features->Equals(instruction_set_features.get())) << "System property features: " << *property_features.get() << "\nFeatures from build: " << *instruction_set_features.get(); + } } } @@ -127,13 +144,7 @@ TEST(InstructionSetFeaturesTest, FeaturesFromHwcap) { << "\nFeatures from build: " << *instruction_set_features.get(); } - -#if defined(__arm__) -TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromAssembly) { - LOG(WARNING) << "Test disabled due to buggy ARM kernels"; -#else TEST(InstructionSetFeaturesTest, FeaturesFromAssembly) { -#endif // Take the default set of instruction features from the build. std::unique_ptr<const InstructionSetFeatures> instruction_set_features( InstructionSetFeatures::FromCppDefines()); diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc index efec993340..11be2a8bc2 100644 --- a/runtime/arch/mips/instruction_set_features_mips.cc +++ b/runtime/arch/mips/instruction_set_features_mips.cc @@ -44,11 +44,7 @@ const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromBitmap(uint32_ } const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCppDefines() { -#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0) - const bool smp = false; -#else const bool smp = true; -#endif // TODO: here we assume the FPU is always 32-bit. const bool fpu_32bit = true; diff --git a/runtime/arch/mips/portable_entrypoints_mips.S b/runtime/arch/mips/portable_entrypoints_mips.S index d7e7a8e96b..8d418e8dd1 100644 --- a/runtime/arch/mips/portable_entrypoints_mips.S +++ b/runtime/arch/mips/portable_entrypoints_mips.S @@ -98,7 +98,7 @@ ENTRY art_portable_invoke_stub lw $a1, 4($sp) # copy arg value for a1 lw $a2, 8($sp) # copy arg value for a2 lw $a3, 12($sp) # copy arg value for a3 - lw $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET($a0) # get pointer to the code + lw $t9, MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32($a0) # get pointer to the code jalr $t9 # call the method sw $zero, 0($sp) # store NULL for method* at bottom of frame move $sp, $fp # restore the stack diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index e878ef7186..482485747a 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -512,7 +512,7 @@ ENTRY art_quick_invoke_stub lw $a1, 4($sp) # copy arg value for a1 lw $a2, 8($sp) # copy arg value for a2 lw $a3, 12($sp) # copy arg value for a3 - lw $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET($a0) # get pointer to the code + lw $t9, MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32($a0) # get pointer to the code jalr $t9 # call the method sw $zero, 0($sp) # store NULL for method* at bottom of frame move $sp, $fp # restore the stack diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc index 32cf909fd1..a12773dc9c 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -70,11 +70,7 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromBitmap(uint32_t } const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCppDefines(bool x86_64) { -#if defined(HAVE_ANDROID_OS) && (ANDROID_SMP == 0) - const bool smp = false; -#else const bool smp = true; -#endif #ifndef __SSSE3__ const bool has_SSSE3 = false; diff --git a/runtime/arch/x86/portable_entrypoints_x86.S b/runtime/arch/x86/portable_entrypoints_x86.S index a7c4124a47..1f0900e86d 100644 --- a/runtime/arch/x86/portable_entrypoints_x86.S +++ b/runtime/arch/x86/portable_entrypoints_x86.S @@ -46,7 +46,7 @@ DEFINE_FUNCTION art_portable_invoke_stub addl LITERAL(12), %esp // pop arguments to memcpy mov 12(%ebp), %eax // move method pointer into eax mov %eax, (%esp) // push method pointer onto stack - call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET(%eax) // call the method + call *MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32(%eax) // call the method mov %ebp, %esp // restore stack pointer POP ebx // pop ebx POP ebp // pop ebp diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 0109a7c553..1ce01c46f1 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -316,7 +316,7 @@ DEFINE_FUNCTION art_quick_invoke_stub mov 4(%esp), %ecx // copy arg1 into ecx mov 8(%esp), %edx // copy arg2 into edx mov 12(%esp), %ebx // copy arg3 into ebx - call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%eax) // call the method + call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32(%eax) // call the method mov %ebp, %esp // restore stack pointer CFI_DEF_CFA_REGISTER(esp) POP ebx // pop ebx @@ -1117,7 +1117,7 @@ DEFINE_FUNCTION art_quick_instrumentation_entry PUSH eax // Save eax which will be clobbered by the callee-save method. subl LITERAL(12), %esp // Align stack. CFI_ADJUST_CFA_OFFSET(12) - pushl 40(%esp) // Pass LR. + pushl FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-4+16(%esp) // Pass LR. CFI_ADJUST_CFA_OFFSET(4) pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current(). CFI_ADJUST_CFA_OFFSET(4) diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index bed7238b09..a80e7d2989 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -518,7 +518,7 @@ DEFINE_FUNCTION art_quick_invoke_stub LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished .Lgpr_setup_finished: - call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. + call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. movq %rbp, %rsp // Restore stack pointer. CFI_DEF_CFA_REGISTER(rsp) POP r9 // Pop r9 - shorty*. @@ -601,7 +601,7 @@ DEFINE_FUNCTION art_quick_invoke_static_stub LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2 LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2 .Lgpr_setup_finished2: - call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. + call *MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64(%rdi) // Call the method. movq %rbp, %rsp // Restore stack pointer. CFI_DEF_CFA_REGISTER(rsp) POP r9 // Pop r9 - shorty*. @@ -1446,7 +1446,7 @@ DEFINE_FUNCTION art_quick_instrumentation_entry movq %rdi, %r12 // Preserve method pointer in a callee-save. movq %gs:THREAD_SELF_OFFSET, %rdx // Pass thread. - movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %r8 // Pass return PC. + movq FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE-8(%rsp), %rcx // Pass return PC. call SYMBOL(artInstrumentationMethodEntryFromCode) // (Method*, Object*, Thread*, LR) diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 26df045a25..4b4c8855d4 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -148,13 +148,21 @@ ADD_TEST_EQ(MIRROR_STRING_OFFSET_OFFSET, art::mirror::String::OffsetOffset().Int ADD_TEST_EQ(MIRROR_ART_METHOD_DEX_CACHE_METHODS_OFFSET, art::mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()) -#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET (32 + MIRROR_OBJECT_HEADER_SIZE) -ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET, - art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset().Int32Value()) +#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32 (48 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32, + art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value()) -#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET (40 + MIRROR_OBJECT_HEADER_SIZE) -ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET, - art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()) +#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32 (40 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32, + art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value()) + +#define MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64 (64 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64, + art::mirror::ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value()) + +#define MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64 (48 + MIRROR_OBJECT_HEADER_SIZE) +ADD_TEST_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64, + art::mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value()) #if defined(__cplusplus) } // End of CheckAsmSupportOffsets. diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index bf091d00d2..0e93eee627 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -27,6 +27,9 @@ namespace art { bool ScopedFlock::Init(const char* filename, std::string* error_msg) { while (true) { + if (file_.get() != nullptr) { + UNUSED(file_->FlushCloseOrErase()); // Ignore result. + } file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR)); if (file_.get() == NULL) { *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno)); @@ -59,7 +62,7 @@ bool ScopedFlock::Init(const char* filename, std::string* error_msg) { } bool ScopedFlock::Init(File* file, std::string* error_msg) { - file_.reset(new File(dup(file->Fd()))); + file_.reset(new File(dup(file->Fd()), true)); if (file_->Fd() == -1) { file_.reset(); *error_msg = StringPrintf("Failed to duplicate open file '%s': %s", @@ -89,6 +92,9 @@ ScopedFlock::~ScopedFlock() { if (file_.get() != NULL) { int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); CHECK_EQ(0, flock_result); + if (file_->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Could not close scoped file lock file."; + } } } diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index f29a7ec974..6e5e7a1582 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -14,28 +14,68 @@ * limitations under the License. */ -#include "base/logging.h" #include "base/unix_file/fd_file.h" + #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include "base/logging.h" + namespace unix_file { -FdFile::FdFile() : fd_(-1), auto_close_(true) { +FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) { } -FdFile::FdFile(int fd) : fd_(fd), auto_close_(true) { +FdFile::FdFile(int fd, bool check_usage) + : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), + fd_(fd), auto_close_(true) { } -FdFile::FdFile(int fd, const std::string& path) : fd_(fd), file_path_(path), auto_close_(true) { +FdFile::FdFile(int fd, const std::string& path, bool check_usage) + : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), + fd_(fd), file_path_(path), auto_close_(true) { CHECK_NE(0U, path.size()); } FdFile::~FdFile() { + if (kCheckSafeUsage && (guard_state_ < GuardState::kNoCheck)) { + if (guard_state_ < GuardState::kFlushed) { + LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly flushed before destruction."; + } + if (guard_state_ < GuardState::kClosed) { + LOG(::art::ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction."; + } + CHECK_GE(guard_state_, GuardState::kClosed); + } if (auto_close_ && fd_ != -1) { - Close(); + if (Close() != 0) { + PLOG(::art::WARNING) << "Failed to close file " << file_path_; + } + } +} + +void FdFile::moveTo(GuardState target, GuardState warn_threshold, const char* warning) { + if (kCheckSafeUsage) { + if (guard_state_ < GuardState::kNoCheck) { + if (warn_threshold < GuardState::kNoCheck && guard_state_ >= warn_threshold) { + LOG(::art::ERROR) << warning; + } + guard_state_ = target; + } + } +} + +void FdFile::moveUp(GuardState target, const char* warning) { + if (kCheckSafeUsage) { + if (guard_state_ < GuardState::kNoCheck) { + if (guard_state_ < target) { + guard_state_ = target; + } else if (target < guard_state_) { + LOG(::art::ERROR) << warning; + } + } } } @@ -54,11 +94,28 @@ bool FdFile::Open(const std::string& path, int flags, mode_t mode) { return false; } file_path_ = path; + static_assert(O_RDONLY == 0, "Readonly flag has unexpected value."); + if (kCheckSafeUsage && (flags & (O_RDWR | O_CREAT | O_WRONLY)) != 0) { + // Start in the base state (not flushed, not closed). + guard_state_ = GuardState::kBase; + } else { + // We are not concerned with read-only files. In that case, proper flushing and closing is + // not important. + guard_state_ = GuardState::kNoCheck; + } return true; } int FdFile::Close() { int result = TEMP_FAILURE_RETRY(close(fd_)); + + // Test here, so the file is closed and not leaked. + if (kCheckSafeUsage) { + CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_ + << " has not been flushed before closing."; + moveUp(GuardState::kClosed, nullptr); + } + if (result == -1) { return -errno; } else { @@ -74,6 +131,7 @@ int FdFile::Flush() { #else int rc = TEMP_FAILURE_RETRY(fsync(fd_)); #endif + moveUp(GuardState::kFlushed, "Flushing closed file."); return (rc == -1) ? -errno : rc; } @@ -92,6 +150,7 @@ int FdFile::SetLength(int64_t new_length) { #else int rc = TEMP_FAILURE_RETRY(ftruncate(fd_, new_length)); #endif + moveTo(GuardState::kBase, GuardState::kClosed, "Truncating closed file."); return (rc == -1) ? -errno : rc; } @@ -107,6 +166,7 @@ int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { #else int rc = TEMP_FAILURE_RETRY(pwrite(fd_, buf, byte_count, offset)); #endif + moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); return (rc == -1) ? -errno : rc; } @@ -135,6 +195,7 @@ bool FdFile::ReadFully(void* buffer, size_t byte_count) { bool FdFile::WriteFully(const void* buffer, size_t byte_count) { const char* ptr = static_cast<const char*>(buffer); + moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); while (byte_count > 0) { ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); if (bytes_written == -1) { @@ -146,4 +207,38 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) { return true; } +void FdFile::Erase() { + TEMP_FAILURE_RETRY(SetLength(0)); + TEMP_FAILURE_RETRY(Flush()); + TEMP_FAILURE_RETRY(Close()); +} + +int FdFile::FlushCloseOrErase() { + int flush_result = TEMP_FAILURE_RETRY(Flush()); + if (flush_result != 0) { + LOG(::art::ERROR) << "CloseOrErase failed while flushing a file."; + Erase(); + return flush_result; + } + int close_result = TEMP_FAILURE_RETRY(Close()); + if (close_result != 0) { + LOG(::art::ERROR) << "CloseOrErase failed while closing a file."; + Erase(); + return close_result; + } + return 0; +} + +int FdFile::FlushClose() { + int flush_result = TEMP_FAILURE_RETRY(Flush()); + if (flush_result != 0) { + LOG(::art::ERROR) << "FlushClose failed while flushing a file."; + } + int close_result = TEMP_FAILURE_RETRY(Close()); + if (close_result != 0) { + LOG(::art::ERROR) << "FlushClose failed while closing a file."; + } + return (flush_result != 0) ? flush_result : close_result; +} + } // namespace unix_file diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 01f4ca2819..8db2ee4a81 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -24,6 +24,9 @@ namespace unix_file { +// If true, check whether Flush and Close are called before destruction. +static constexpr bool kCheckSafeUsage = true; + // A RandomAccessFile implementation backed by a file descriptor. // // Not thread safe. @@ -32,8 +35,8 @@ class FdFile : public RandomAccessFile { FdFile(); // Creates an FdFile using the given file descriptor. Takes ownership of the // file descriptor. (Use DisableAutoClose to retain ownership.) - explicit FdFile(int fd); - explicit FdFile(int fd, const std::string& path); + explicit FdFile(int fd, bool checkUsage); + explicit FdFile(int fd, const std::string& path, bool checkUsage); // Destroys an FdFile, closing the file descriptor if Close hasn't already // been called. (If you care about the return value of Close, call it @@ -47,12 +50,21 @@ class FdFile : public RandomAccessFile { bool Open(const std::string& file_path, int flags, mode_t mode); // RandomAccessFile API. - virtual int Close(); - virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const; - virtual int SetLength(int64_t new_length); + virtual int Close() WARN_UNUSED; + virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const WARN_UNUSED; + virtual int SetLength(int64_t new_length) WARN_UNUSED; virtual int64_t GetLength() const; - virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset); - virtual int Flush(); + virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset) WARN_UNUSED; + virtual int Flush() WARN_UNUSED; + + // Short for SetLength(0); Flush(); Close(); + void Erase(); + + // Try to Flush(), then try to Close(); If either fails, call Erase(). + int FlushCloseOrErase() WARN_UNUSED; + + // Try to Flush and Close(). Attempts both, but returns the first error. + int FlushClose() WARN_UNUSED; // Bonus API. int Fd() const; @@ -61,8 +73,35 @@ class FdFile : public RandomAccessFile { return file_path_; } void DisableAutoClose(); - bool ReadFully(void* buffer, size_t byte_count); - bool WriteFully(const void* buffer, size_t byte_count); + bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED; + bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED; + + // This enum is public so that we can define the << operator over it. + enum class GuardState { + kBase, // Base, file has not been flushed or closed. + kFlushed, // File has been flushed, but not closed. + kClosed, // File has been flushed and closed. + kNoCheck // Do not check for the current file instance. + }; + + protected: + // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the + // given warning if the current state is or exceeds warn_threshold. + void moveTo(GuardState target, GuardState warn_threshold, const char* warning); + + // If the guard state indicates checking (<kNoCheck), and is below the target state "target", go + // to "target." If the current state is higher (excluding kNoCheck) than the trg state, print the + // warning. + void moveUp(GuardState target, const char* warning); + + // Forcefully sets the state to the given one. This can overwrite kNoCheck. + void resetGuard(GuardState new_state) { + if (kCheckSafeUsage) { + guard_state_ = new_state; + } + } + + GuardState guard_state_; private: int fd_; @@ -72,6 +111,8 @@ class FdFile : public RandomAccessFile { DISALLOW_COPY_AND_ASSIGN(FdFile); }; +std::ostream& operator<<(std::ostream& os, const FdFile::GuardState& kind); + } // namespace unix_file #endif // ART_RUNTIME_BASE_UNIX_FILE_FD_FILE_H_ diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc index 3481f2ff9f..a7e5b96460 100644 --- a/runtime/base/unix_file/fd_file_test.cc +++ b/runtime/base/unix_file/fd_file_test.cc @@ -24,7 +24,7 @@ namespace unix_file { class FdFileTest : public RandomAccessFileTest { protected: virtual RandomAccessFile* MakeTestFile() { - return new FdFile(fileno(tmpfile())); + return new FdFile(fileno(tmpfile()), false); } }; @@ -53,6 +53,7 @@ TEST_F(FdFileTest, OpenClose) { ASSERT_TRUE(file.Open(good_path, O_CREAT | O_WRONLY)); EXPECT_GE(file.Fd(), 0); EXPECT_TRUE(file.IsOpened()); + EXPECT_EQ(0, file.Flush()); EXPECT_EQ(0, file.Close()); EXPECT_EQ(-1, file.Fd()); EXPECT_FALSE(file.IsOpened()); @@ -60,7 +61,7 @@ TEST_F(FdFileTest, OpenClose) { EXPECT_GE(file.Fd(), 0); EXPECT_TRUE(file.IsOpened()); - file.Close(); + ASSERT_EQ(file.Close(), 0); ASSERT_EQ(unlink(good_path.c_str()), 0); } diff --git a/runtime/base/unix_file/random_access_file_test.h b/runtime/base/unix_file/random_access_file_test.h index 0002433628..e7ace4caaa 100644 --- a/runtime/base/unix_file/random_access_file_test.h +++ b/runtime/base/unix_file/random_access_file_test.h @@ -76,6 +76,8 @@ class RandomAccessFileTest : public testing::Test { ASSERT_EQ(content.size(), static_cast<uint64_t>(file->Write(content.data(), content.size(), 0))); TestReadContent(content, file.get()); + + CleanUp(file.get()); } void TestReadContent(const std::string& content, RandomAccessFile* file) { @@ -131,6 +133,8 @@ class RandomAccessFileTest : public testing::Test { ASSERT_EQ(new_length, file->GetLength()); ASSERT_TRUE(ReadString(file.get(), &new_content)); ASSERT_EQ('\0', new_content[new_length - 1]); + + CleanUp(file.get()); } void TestWrite() { @@ -163,6 +167,11 @@ class RandomAccessFileTest : public testing::Test { ASSERT_EQ(file->GetLength(), new_length); ASSERT_TRUE(ReadString(file.get(), &new_content)); ASSERT_EQ(std::string("hello\0hello", new_length), new_content); + + CleanUp(file.get()); + } + + virtual void CleanUp(RandomAccessFile* file ATTRIBUTE_UNUSED) { } protected: diff --git a/runtime/base/unix_file/random_access_file_utils_test.cc b/runtime/base/unix_file/random_access_file_utils_test.cc index 63179220a2..9457d22424 100644 --- a/runtime/base/unix_file/random_access_file_utils_test.cc +++ b/runtime/base/unix_file/random_access_file_utils_test.cc @@ -37,14 +37,14 @@ TEST_F(RandomAccessFileUtilsTest, CopyFile) { } TEST_F(RandomAccessFileUtilsTest, BadSrc) { - FdFile src(-1); + FdFile src(-1, false); StringFile dst; ASSERT_FALSE(CopyFile(src, &dst)); } TEST_F(RandomAccessFileUtilsTest, BadDst) { StringFile src; - FdFile dst(-1); + FdFile dst(-1, false); // We need some source content to trigger a write. // Copying an empty file is a no-op. diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index 9d2d59c9a4..821d613040 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -53,7 +53,7 @@ class CheckReferenceMapVisitor : public StackVisitor { void CheckReferences(int* registers, int number_of_references, uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (GetMethod()->IsOptimized()) { + if (GetMethod()->IsOptimized(sizeof(void*))) { CheckOptimizedMethod(registers, number_of_references, native_pc_offset); } else { CheckQuickMethod(registers, number_of_references, native_pc_offset); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 84cbcdc466..e1b79c9f87 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -243,7 +243,8 @@ ClassLinker::ClassLinker(InternTable* intern_table) portable_imt_conflict_trampoline_(nullptr), quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), - quick_to_interpreter_bridge_trampoline_(nullptr) { + quick_to_interpreter_bridge_trampoline_(nullptr), + image_pointer_size_(sizeof(void*)) { memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*)); } @@ -378,10 +379,9 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize()))); CHECK(java_lang_reflect_ArtMethod.Get() != nullptr); - java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize()); + java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*))); SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get()); java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self); - mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get()); // Set up array classes for string, field, method @@ -407,8 +407,7 @@ void ClassLinker::InitWithoutImage(const std::vector<const DexFile*>& boot_class // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses // these roots. CHECK_NE(0U, boot_class_path.size()); - for (size_t i = 0; i != boot_class_path.size(); ++i) { - const DexFile* dex_file = boot_class_path[i]; + for (const DexFile* dex_file : boot_class_path) { CHECK(dex_file != nullptr); AppendToBootClassPath(self, *dex_file); } @@ -1682,6 +1681,20 @@ void ClassLinker::InitFromImage() { // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live // bitmap walk. mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod)); + size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize(); + if (!Runtime::Current()->IsCompiler()) { + // Compiler supports having an image with a different pointer size than the runtime. This + // happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may + // also use 32 bit dex2oat on a system with 64 bit apps. + CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*))) + << sizeof(void*); + } + if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) { + image_pointer_size_ = 4; + } else { + CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8)); + image_pointer_size_ = 8; + } // Set entry point to interpreter if in InterpretOnly mode. if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { @@ -1695,7 +1708,7 @@ void ClassLinker::InitFromImage() { // reinit array_iftable_ from any array class instance, they should be == array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable()); - DCHECK(array_iftable_.Read() == GetClassRoot(kBooleanArrayClass)->GetIfTable()); + DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); // String class root was set above mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField)); @@ -2047,6 +2060,7 @@ mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlready Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader) { + // Can we special case for a well understood PathClassLoader with the BootClassLoader as parent? if (class_loader->GetClass() != soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) || class_loader->GetParent()->GetClass() != @@ -2058,17 +2072,21 @@ mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlready if (pair.second != nullptr) { mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr); if (klass != nullptr) { - return EnsureResolved(self, descriptor, klass); + // May return null if resolution on another thread fails. + klass = EnsureResolved(self, descriptor, klass); + } else { + // May OOME. + klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, + *pair.second); } - klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, - *pair.second); - if (klass != nullptr) { - return klass; + if (klass == nullptr) { + CHECK(self->IsExceptionPending()) << descriptor; + self->ClearException(); } - CHECK(self->IsExceptionPending()) << descriptor; - self->ClearException(); + return klass; } else { - // RegisterDexFile may allocate dex caches (and cause thread suspension). + // Handle as if this is the child PathClassLoader. + // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension). StackHandleScope<3> hs(self); // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. // We need to get the DexPathList and loop through it. @@ -2125,8 +2143,9 @@ mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlready } } } + self->AssertNoPendingException(); + return nullptr; } - return nullptr; } mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, @@ -5216,8 +5235,10 @@ struct LinkFieldsComparator { // Primitive types differ but sizes match. Arbitrarily order by primitive type. return type1 < type2; } - // same basic group? then sort by string. - return strcmp(field1->GetName(), field2->GetName()) < 0; + // Same basic group? Then sort by dex field index. This is guaranteed to be sorted + // by name and for equal names by type id index. + // NOTE: This works also for proxies. Their static fields are assigned appropriate indexes. + return field1->GetDexFieldIndex() < field2->GetDexFieldIndex(); } }; @@ -5310,14 +5331,18 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_ } else { klass->SetNumReferenceInstanceFields(num_reference_fields); if (!klass->IsVariableSize()) { - std::string temp; - DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp); - size_t previous_size = klass->GetObjectSize(); - if (previous_size != 0) { - // Make sure that we didn't originally have an incorrect size. - CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp); + if (klass->DescriptorEquals("Ljava/lang/reflect/ArtMethod;")) { + klass->SetObjectSize(mirror::ArtMethod::InstanceSize(sizeof(void*))); + } else { + std::string temp; + DCHECK_GE(size, sizeof(mirror::Object)) << klass->GetDescriptor(&temp); + size_t previous_size = klass->GetObjectSize(); + if (previous_size != 0) { + // Make sure that we didn't originally have an incorrect size. + CHECK_EQ(previous_size, size) << klass->GetDescriptor(&temp); + } + klass->SetObjectSize(size); } - klass->SetObjectSize(size); } } @@ -5342,7 +5367,9 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_ } if (i != 0) { mirror::ArtField* prev_field = fields->Get(i - 1u); - CHECK_LT(strcmp(prev_field->GetName(), field->GetName()), 0); + // NOTE: The field names can be the same. This is not possible in the Java language + // but it's valid Java/dex bytecode and for example proguard can generate such bytecode. + CHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0); } Primitive::Type type = field->GetTypeAsPrimitiveType(); bool is_primitive = type != Primitive::kPrimNot; @@ -5368,7 +5395,6 @@ bool ClassLinker::LinkFields(Thread* self, Handle<mirror::Class> klass, bool is_ } CHECK_EQ(current_ref_offset.Uint32Value(), end_ref_offset.Uint32Value()); } - return true; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 385f135192..b78d0b5bc3 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -117,8 +117,8 @@ class ClassLinker { Handle<mirror::ClassLoader> class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Find a class in the path class loader, loading it if necessary. Hash function is supposed to - // be ComputeModifiedUtf8Hash(descriptor). + // Find a class in the path class loader, loading it if necessary without using JNI. Hash + // function is supposed to be ComputeModifiedUtf8Hash(descriptor). mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader) @@ -805,6 +805,9 @@ class ClassLinker { const void* quick_generic_jni_trampoline_; const void* quick_to_interpreter_bridge_trampoline_; + // Image pointer size. + size_t image_pointer_size_; + friend class ImageWriter; // for GetClassRoots friend class ImageDumper; // for FindOpenedOatFileFromOatLocation friend class ElfPatcher; // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index ba5aa3d75a..0c86761979 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -499,11 +499,6 @@ struct ArtMethodOffsets : public CheckOffsets<mirror::ArtMethod> { offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_cache_strings_), "dexCacheStrings")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_code_item_offset_), "dexCodeItemOffset")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, dex_method_index_), "dexMethodIndex")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_interpreter_), "entryPointFromInterpreter")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_jni_), "entryPointFromJni")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_portable_compiled_code_), "entryPointFromPortableCompiledCode")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, entry_point_from_quick_compiled_code_), "entryPointFromQuickCompiledCode")); - offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, gc_map_), "gcMap")); offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::ArtMethod, method_index_), "methodIndex")); }; }; diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 6e3ebc24d8..03b33e962a 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -59,7 +59,7 @@ ScratchFile::ScratchFile() { filename_ += "/TmpFile-XXXXXX"; int fd = mkstemp(&filename_[0]); CHECK_NE(-1, fd); - file_.reset(new File(fd, GetFilename())); + file_.reset(new File(fd, GetFilename(), true)); } ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) { @@ -67,7 +67,7 @@ ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) { filename_ += suffix; int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666); CHECK_NE(-1, fd); - file_.reset(new File(fd, GetFilename())); + file_.reset(new File(fd, GetFilename(), true)); } ScratchFile::ScratchFile(File* file) { @@ -88,6 +88,11 @@ void ScratchFile::Unlink() { if (!OS::FileExists(filename_.c_str())) { return; } + if (file_.get() != nullptr) { + if (file_->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Error closing scratch file."; + } + } int unlink_result = unlink(filename_.c_str()); CHECK_EQ(0, unlink_result); } diff --git a/runtime/debugger.cc b/runtime/debugger.cc index e2f6085ae2..ef5db2d5ea 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -183,16 +183,20 @@ class AllocRecord { class Breakpoint { public: - Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, bool need_full_deoptimization) + Breakpoint(mirror::ArtMethod* method, uint32_t dex_pc, + DeoptimizationRequest::Kind deoptimization_kind) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : method_(nullptr), dex_pc_(dex_pc), need_full_deoptimization_(need_full_deoptimization) { + : method_(nullptr), dex_pc_(dex_pc), deoptimization_kind_(deoptimization_kind) { + CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing || + deoptimization_kind_ == DeoptimizationRequest::kSelectiveDeoptimization || + deoptimization_kind_ == DeoptimizationRequest::kFullDeoptimization); ScopedObjectAccessUnchecked soa(Thread::Current()); method_ = soa.EncodeMethod(method); } Breakpoint(const Breakpoint& other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : method_(nullptr), dex_pc_(other.dex_pc_), - need_full_deoptimization_(other.need_full_deoptimization_) { + deoptimization_kind_(other.deoptimization_kind_) { ScopedObjectAccessUnchecked soa(Thread::Current()); method_ = soa.EncodeMethod(other.Method()); } @@ -206,8 +210,8 @@ class Breakpoint { return dex_pc_; } - bool NeedFullDeoptimization() const { - return need_full_deoptimization_; + DeoptimizationRequest::Kind GetDeoptimizationKind() const { + return deoptimization_kind_; } private: @@ -216,7 +220,7 @@ class Breakpoint { uint32_t dex_pc_; // Indicates whether breakpoint needs full deoptimization or selective deoptimization. - bool need_full_deoptimization_; + DeoptimizationRequest::Kind deoptimization_kind_; }; static std::ostream& operator<<(std::ostream& os, const Breakpoint& rhs) @@ -736,6 +740,12 @@ bool Dbg::IsDisposed() { return gDisposed; } +bool Dbg::RequiresDeoptimization() { + // We don't need deoptimization if everything runs with interpreter after + // enabling -Xint mode. + return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly(); +} + void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -768,7 +778,9 @@ void Dbg::GoActive() { Thread* self = Thread::Current(); ThreadState old_state = self->SetStateUnsafe(kRunnable); CHECK_NE(old_state, kRunnable); - runtime->GetInstrumentation()->EnableDeoptimization(); + if (RequiresDeoptimization()) { + runtime->GetInstrumentation()->EnableDeoptimization(); + } instrumentation_events_ = 0; gDebuggerActive = true; CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); @@ -806,7 +818,9 @@ void Dbg::Disconnected() { instrumentation_events_); instrumentation_events_ = 0; } - runtime->GetInstrumentation()->DisableDeoptimization(); + if (RequiresDeoptimization()) { + runtime->GetInstrumentation()->DisableDeoptimization(); + } gDebuggerActive = false; } gRegistry->Clear(); @@ -3035,9 +3049,11 @@ void Dbg::ProcessDeoptimizationRequest(const DeoptimizationRequest& request) { } void Dbg::DelayFullUndeoptimization() { - MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); - ++delayed_full_undeoptimization_count_; - DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_); + if (RequiresDeoptimization()) { + MutexLock mu(Thread::Current(), *Locks::deoptimization_lock_); + ++delayed_full_undeoptimization_count_; + DCHECK_LE(delayed_full_undeoptimization_count_, full_deoptimization_event_count_); + } } void Dbg::ProcessDelayedFullUndeoptimizations() { @@ -3196,64 +3212,101 @@ static const Breakpoint* FindFirstBreakpointForMethod(mirror::ArtMethod* m) } // Sanity checks all existing breakpoints on the same method. -static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, bool need_full_deoptimization) +static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, + DeoptimizationRequest::Kind deoptimization_kind) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::breakpoint_lock_) { for (const Breakpoint& breakpoint : gBreakpoints) { - CHECK_EQ(need_full_deoptimization, breakpoint.NeedFullDeoptimization()); + if (breakpoint.Method() == m) { + CHECK_EQ(deoptimization_kind, breakpoint.GetDeoptimizationKind()); + } } - if (need_full_deoptimization) { + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) { // We should have deoptimized everything but not "selectively" deoptimized this method. - CHECK(Runtime::Current()->GetInstrumentation()->AreAllMethodsDeoptimized()); - CHECK(!Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); - } else { + CHECK(instrumentation->AreAllMethodsDeoptimized()); + CHECK(!instrumentation->IsDeoptimized(m)); + } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) { // We should have "selectively" deoptimized this method. // Note: while we have not deoptimized everything for this method, we may have done it for // another event. - CHECK(Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); + CHECK(instrumentation->IsDeoptimized(m)); + } else { + // This method does not require deoptimization. + CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing); + CHECK(!instrumentation->IsDeoptimized(m)); } } -// Installs a breakpoint at the specified location. Also indicates through the deoptimization -// request if we need to deoptimize. -void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) { - Thread* const self = Thread::Current(); - mirror::ArtMethod* m = FromMethodId(location->method_id); - DCHECK(m != nullptr) << "No method for method id " << location->method_id; - +static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self, + mirror::ArtMethod* m) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (!Dbg::RequiresDeoptimization()) { + // We already run in interpreter-only mode so we don't need to deoptimize anything. + VLOG(jdwp) << "No need for deoptimization when fully running with interpreter for method " + << PrettyMethod(m); + return DeoptimizationRequest::kNothing; + } const Breakpoint* existing_breakpoint; { ReaderMutexLock mu(self, *Locks::breakpoint_lock_); existing_breakpoint = FindFirstBreakpointForMethod(m); } - bool need_full_deoptimization; if (existing_breakpoint == nullptr) { // There is no breakpoint on this method yet: we need to deoptimize. If this method may be // inlined, we deoptimize everything; otherwise we deoptimize only this method. // Note: IsMethodPossiblyInlined goes into the method verifier and may cause thread suspension. // Therefore we must not hold any lock when we call it. - need_full_deoptimization = IsMethodPossiblyInlined(self, m); + bool need_full_deoptimization = IsMethodPossiblyInlined(self, m); if (need_full_deoptimization) { - req->SetKind(DeoptimizationRequest::kFullDeoptimization); - req->SetMethod(nullptr); + VLOG(jdwp) << "Need full deoptimization because of possible inlining of method " + << PrettyMethod(m); + return DeoptimizationRequest::kFullDeoptimization; } else { - req->SetKind(DeoptimizationRequest::kSelectiveDeoptimization); - req->SetMethod(m); + // We don't need to deoptimize if the method has not been compiled. + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr; + if (is_compiled) { + VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m); + return DeoptimizationRequest::kSelectiveDeoptimization; + } else { + // Method is not compiled: we don't need to deoptimize. + VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m); + return DeoptimizationRequest::kNothing; + } } } else { // There is at least one breakpoint for this method: we don't need to deoptimize. - req->SetKind(DeoptimizationRequest::kNothing); - req->SetMethod(nullptr); - - need_full_deoptimization = existing_breakpoint->NeedFullDeoptimization(); + // Let's check that all breakpoints are configured the same way for deoptimization. + VLOG(jdwp) << "Breakpoint already set: no deoptimization is required"; + DeoptimizationRequest::Kind deoptimization_kind = existing_breakpoint->GetDeoptimizationKind(); if (kIsDebugBuild) { ReaderMutexLock mu(self, *Locks::breakpoint_lock_); - SanityCheckExistingBreakpoints(m, need_full_deoptimization); + SanityCheckExistingBreakpoints(m, deoptimization_kind); } + return DeoptimizationRequest::kNothing; + } +} + +// Installs a breakpoint at the specified location. Also indicates through the deoptimization +// request if we need to deoptimize. +void Dbg::WatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequest* req) { + Thread* const self = Thread::Current(); + mirror::ArtMethod* m = FromMethodId(location->method_id); + DCHECK(m != nullptr) << "No method for method id " << location->method_id; + + const DeoptimizationRequest::Kind deoptimization_kind = GetRequiredDeoptimizationKind(self, m); + req->SetKind(deoptimization_kind); + if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) { + req->SetMethod(m); + } else { + CHECK(deoptimization_kind == DeoptimizationRequest::kNothing || + deoptimization_kind == DeoptimizationRequest::kFullDeoptimization); + req->SetMethod(nullptr); } { WriterMutexLock mu(self, *Locks::breakpoint_lock_); - gBreakpoints.push_back(Breakpoint(m, location->dex_pc, need_full_deoptimization)); + gBreakpoints.push_back(Breakpoint(m, location->dex_pc, deoptimization_kind)); VLOG(jdwp) << "Set breakpoint #" << (gBreakpoints.size() - 1) << ": " << gBreakpoints[gBreakpoints.size() - 1]; } @@ -3265,12 +3318,13 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ WriterMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); mirror::ArtMethod* m = FromMethodId(location->method_id); DCHECK(m != nullptr) << "No method for method id " << location->method_id; - bool need_full_deoptimization = false; + DeoptimizationRequest::Kind deoptimization_kind = DeoptimizationRequest::kNothing; for (size_t i = 0, e = gBreakpoints.size(); i < e; ++i) { if (gBreakpoints[i].DexPc() == location->dex_pc && gBreakpoints[i].Method() == m) { VLOG(jdwp) << "Removed breakpoint #" << i << ": " << gBreakpoints[i]; - need_full_deoptimization = gBreakpoints[i].NeedFullDeoptimization(); - DCHECK_NE(need_full_deoptimization, Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); + deoptimization_kind = gBreakpoints[i].GetDeoptimizationKind(); + DCHECK_EQ(deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization, + Runtime::Current()->GetInstrumentation()->IsDeoptimized(m)); gBreakpoints.erase(gBreakpoints.begin() + i); break; } @@ -3278,21 +3332,26 @@ void Dbg::UnwatchLocation(const JDWP::JdwpLocation* location, DeoptimizationRequ const Breakpoint* const existing_breakpoint = FindFirstBreakpointForMethod(m); if (existing_breakpoint == nullptr) { // There is no more breakpoint on this method: we need to undeoptimize. - if (need_full_deoptimization) { + if (deoptimization_kind == DeoptimizationRequest::kFullDeoptimization) { // This method required full deoptimization: we need to undeoptimize everything. req->SetKind(DeoptimizationRequest::kFullUndeoptimization); req->SetMethod(nullptr); - } else { + } else if (deoptimization_kind == DeoptimizationRequest::kSelectiveDeoptimization) { // This method required selective deoptimization: we need to undeoptimize only that method. req->SetKind(DeoptimizationRequest::kSelectiveUndeoptimization); req->SetMethod(m); + } else { + // This method had no need for deoptimization: do nothing. + CHECK_EQ(deoptimization_kind, DeoptimizationRequest::kNothing); + req->SetKind(DeoptimizationRequest::kNothing); + req->SetMethod(nullptr); } } else { // There is at least one breakpoint for this method: we don't need to undeoptimize. req->SetKind(DeoptimizationRequest::kNothing); req->SetMethod(nullptr); if (kIsDebugBuild) { - SanityCheckExistingBreakpoints(m, need_full_deoptimization); + SanityCheckExistingBreakpoints(m, deoptimization_kind); } } } diff --git a/runtime/debugger.h b/runtime/debugger.h index 488ba7f728..92031634cd 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -523,6 +523,9 @@ class Dbg { LOCKS_EXCLUDED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Indicates whether we need deoptimization for debugging. + static bool RequiresDeoptimization(); + // Records deoptimization request in the queue. static void RequestDeoptimization(const DeoptimizationRequest& req) LOCKS_EXCLUDED(Locks::deoptimization_lock_) diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 134e284999..b304779568 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -146,6 +146,9 @@ static const DexFile* OpenDexFileBase64(const char* base64, if (!file->WriteFully(dex_bytes.get(), length)) { PLOG(FATAL) << "Failed to write base64 as dex file"; } + if (file->FlushCloseOrErase() != 0) { + PLOG(FATAL) << "Could not flush and close test file."; + } file.reset(); // read dex file diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index addd94833e..ec1e5f02fe 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -115,6 +115,9 @@ static const DexFile* OpenDexFileBase64(const char* base64, const char* location if (!file->WriteFully(dex_bytes.get(), length)) { PLOG(FATAL) << "Failed to write base64 as dex file"; } + if (file->FlushCloseOrErase() != 0) { + PLOG(FATAL) << "Could not flush and close test file."; + } file.reset(); // read dex file @@ -177,6 +180,9 @@ static const DexFile* FixChecksumAndOpen(uint8_t* bytes, size_t length, const ch if (!file->WriteFully(bytes, length)) { PLOG(FATAL) << "Failed to write base64 as dex file"; } + if (file->FlushCloseOrErase() != 0) { + PLOG(FATAL) << "Could not flush and close test file."; + } file.reset(); // read dex file diff --git a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc index 00f5cd5eb1..54dbd8c770 100644 --- a/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_instrumentation_entrypoints.cc @@ -35,9 +35,9 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(mirror::ArtMethod* if (instrumentation->IsDeoptimized(method)) { result = GetQuickToInterpreterBridge(); } else { - result = instrumentation->GetQuickCodeFor(method); + result = instrumentation->GetQuickCodeFor(method, sizeof(void*)); + DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result)); } - DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result)); bool interpreter_entry = (result == GetQuickToInterpreterBridge()); instrumentation->PushInstrumentationStackFrame(self, method->IsStatic() ? nullptr : this_object, method, lr, interpreter_entry); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index c4bc969038..0b7d382deb 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1659,7 +1659,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, *(sp32 - 1) = cookie; // Retrieve the stored native code. - const void* nativeCode = called->GetNativeMethod(); + void* nativeCode = called->GetEntryPointFromJni(); // There are two cases for the content of nativeCode: // 1) Pointer to the native function. diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index ab3ec62d69..835485c351 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -360,7 +360,8 @@ bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool che // at the return PC address. if (true || kIsDebugBuild) { VLOG(signals) << "looking for dex pc for return pc " << std::hex << return_pc; - const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj); + const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj, + sizeof(void*)); uint32_t sought_offset = return_pc - reinterpret_cast<uintptr_t>(code); VLOG(signals) << "pc offset: " << std::hex << sought_offset; } diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 991b956f80..7c2474f3f9 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -1772,7 +1772,7 @@ void RosAlloc::Initialize() { if (i < 4) { numOfPages[i] = 1; } else if (i < 8) { - numOfPages[i] = 2; + numOfPages[i] = 1; } else if (i < 16) { numOfPages[i] = 4; } else if (i < 32) { diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 431686a977..3269e102bc 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -414,8 +414,7 @@ class RosAlloc { // We use thread-local runs for the size Brackets whose indexes // are less than this index. We use shared (current) runs for the rest. - - static const size_t kNumThreadLocalSizeBrackets = 11; + static const size_t kNumThreadLocalSizeBrackets = 8; private: // The base address of the memory region that's managed by this allocator. diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 0e67978d05..0cceaa4467 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -96,6 +96,8 @@ static const size_t kDefaultMarkStackSize = 64 * KB; static const char* kDlMallocSpaceName[2] = {"main dlmalloc space", "main dlmalloc space 1"}; static const char* kRosAllocSpaceName[2] = {"main rosalloc space", "main rosalloc space 1"}; static const char* kMemMapSpaceName[2] = {"main space", "main space 1"}; +static const char* kNonMovingSpaceName = "non moving space"; +static const char* kZygoteSpaceName = "zygote space"; static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, @@ -232,9 +234,13 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max +-main alloc space2 / bump space 2 (capacity_)+- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- */ + // We don't have hspace compaction enabled with GSS. + if (foreground_collector_type_ == kCollectorTypeGSS) { + use_homogeneous_space_compaction_for_oom_ = false; + } bool support_homogeneous_space_compaction = background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact || - use_homogeneous_space_compaction_for_oom; + use_homogeneous_space_compaction_for_oom_; // We may use the same space the main space for the non moving space if we don't need to compact // from the main space. // This is not the case if we support homogeneous compaction or have a moving background @@ -254,10 +260,14 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max std::string error_str; std::unique_ptr<MemMap> non_moving_space_mem_map; if (separate_non_moving_space) { + // If we are the zygote, the non moving space becomes the zygote space when we run + // PreZygoteFork the first time. In this case, call the map "zygote space" since we can't + // rename the mem map later. + const char* space_name = is_zygote ? kZygoteSpaceName: kNonMovingSpaceName; // Reserve the non moving mem map before the other two since it needs to be at a specific // address. non_moving_space_mem_map.reset( - MemMap::MapAnonymous("non moving space", requested_alloc_space_begin, + MemMap::MapAnonymous(space_name, requested_alloc_space_begin, non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str)); CHECK(non_moving_space_mem_map != nullptr) << error_str; // Try to reserve virtual memory at a lower address if we have a separate non moving space. @@ -1972,7 +1982,8 @@ void Heap::PreZygoteFork() { // from this point on. RemoveRememberedSet(old_alloc_space); } - zygote_space_ = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_, + // Remaining space becomes the new non moving space. + zygote_space_ = old_alloc_space->CreateZygoteSpace(kNonMovingSpaceName, low_memory_mode_, &non_moving_space_); CHECK(!non_moving_space_->CanMoveObjects()); if (same_space) { diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index b23212842a..071997f6b5 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -74,17 +74,17 @@ static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) // out-of-date. We also don't really care if this fails since it is just a convenience. // Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c // Note this should only be used during first boot. -static void RealPruneDexCache(const std::string& cache_dir_path); +static void RealPruneDalvikCache(const std::string& cache_dir_path); -static void PruneDexCache(InstructionSet isa) { +static void PruneDalvikCache(InstructionSet isa) { CHECK_NE(isa, kNone); // Prune the base /data/dalvik-cache. - RealPruneDexCache(GetDalvikCacheOrDie(".", false)); + RealPruneDalvikCache(GetDalvikCacheOrDie(".", false)); // Prune /data/dalvik-cache/<isa>. - RealPruneDexCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false)); + RealPruneDalvikCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false)); } -static void RealPruneDexCache(const std::string& cache_dir_path) { +static void RealPruneDalvikCache(const std::string& cache_dir_path) { if (!OS::DirectoryExists(cache_dir_path.c_str())) { return; } @@ -118,6 +118,28 @@ static void RealPruneDexCache(const std::string& cache_dir_path) { CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory."; } +// We write out an empty file to the zygote's ISA specific cache dir at the start of +// every zygote boot and delete it when the boot completes. If we find a file already +// present, it usually means the boot didn't complete. We wipe the entire dalvik +// cache if that's the case. +static void MarkZygoteStart(const InstructionSet isa) { + const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false); + const std::string boot_marker = isa_subdir + "/.booting"; + + if (OS::FileExists(boot_marker.c_str())) { + LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache"; + RealPruneDalvikCache(isa_subdir); + } + + VLOG(startup) << "Creating boot start marker: " << boot_marker; + std::unique_ptr<File> f(OS::CreateEmptyFile(boot_marker.c_str())); + if (f.get() != nullptr) { + if (f->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Failed to write boot marker."; + } + } +} + static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa, std::string* error_msg) { const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString()); @@ -130,7 +152,7 @@ static bool GenerateImage(const std::string& image_filename, InstructionSet imag // We should clean up so we are more likely to have room for the image. if (Runtime::Current()->IsZygote()) { LOG(INFO) << "Pruning dalvik-cache since we are generating an image and will need to recompile"; - PruneDexCache(image_isa); + PruneDalvikCache(image_isa); } std::vector<std::string> arg_vector; @@ -232,7 +254,7 @@ static bool RelocateImage(const char* image_location, const char* dest_filename, // We should clean up so we are more likely to have room for the image. if (Runtime::Current()->IsZygote()) { LOG(INFO) << "Pruning dalvik-cache since we are relocating an image and will need to recompile"; - PruneDexCache(isa); + PruneDalvikCache(isa); } std::string patchoat(Runtime::Current()->GetPatchoatExecutable()); @@ -427,6 +449,10 @@ ImageSpace* ImageSpace::Create(const char* image_location, &has_system, &cache_filename, &dalvik_cache_exists, &has_cache, &is_global_cache); + if (Runtime::Current()->IsZygote()) { + MarkZygoteStart(image_isa); + } + ImageSpace* space; bool relocate = Runtime::Current()->ShouldRelocate(); bool can_compile = Runtime::Current()->IsImageDex2OatEnabled(); @@ -475,7 +501,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, // Since ImageCreationAllowed was true above, we are the zygote // and therefore the only process expected to generate these for // the device. - PruneDexCache(image_isa); + PruneDalvikCache(image_isa); return nullptr; } } @@ -530,7 +556,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, "but image failed to load: %s", image_location, cache_filename.c_str(), system_filename.c_str(), error_msg->c_str()); - PruneDexCache(image_isa); + PruneDalvikCache(image_isa); return nullptr; } else if (is_system) { // If the /system file exists, it should be up-to-date, don't try to generate it. @@ -558,13 +584,13 @@ ImageSpace* ImageSpace::Create(const char* image_location, // Since ImageCreationAllowed was true above, we are the zygote // and therefore the only process expected to generate these for // the device. - PruneDexCache(image_isa); + PruneDalvikCache(image_isa); return nullptr; } else { // Check whether there is enough space left over after we have generated the image. if (!CheckSpace(cache_filename, error_msg)) { // No. Delete the generated image and try to run out of the dex files. - PruneDexCache(image_isa); + PruneDalvikCache(image_isa); return nullptr; } diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 14d7432508..3069581522 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -475,9 +475,14 @@ class Hprof { } } - std::unique_ptr<File> file(new File(out_fd, filename_)); + std::unique_ptr<File> file(new File(out_fd, filename_, true)); okay = file->WriteFully(header_data_ptr_, header_data_size_) && - file->WriteFully(body_data_ptr_, body_data_size_); + file->WriteFully(body_data_ptr_, body_data_size_); + if (okay) { + okay = file->FlushCloseOrErase() == 0; + } else { + file->Erase(); + } if (!okay) { std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", filename_.c_str(), strerror(errno))); diff --git a/runtime/image.cc b/runtime/image.cc index aee84bc3c0..b83eeb16d0 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -24,7 +24,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '2', '\0' }; +const uint8_t ImageHeader::kImageVersion[] = { '0', '1', '3', '\0' }; ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 003e1601ce..639b0f0766 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -869,10 +869,10 @@ void Instrumentation::DisableMethodTracing() { ConfigureStubs(false, false); } -const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method) const { +const void* Instrumentation::GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const { Runtime* runtime = Runtime::Current(); if (LIKELY(!instrumentation_stubs_installed_)) { - const void* code = method->GetEntryPointFromQuickCompiledCode(); + const void* code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); DCHECK(code != nullptr); ClassLinker* class_linker = runtime->GetClassLinker(); if (LIKELY(!class_linker->IsQuickResolutionStub(code) && diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 369039d39f..effa9f7b2e 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -200,7 +200,7 @@ class Instrumentation { // Get the quick code for the given method. More efficient than asking the class linker as it // will short-cut to GetCode if instrumentation and static method resolution stubs aren't // installed. - const void* GetQuickCodeFor(mirror::ArtMethod* method) const + const void* GetQuickCodeFor(mirror::ArtMethod* method, size_t pointer_size) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ForceInterpretOnly() { diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 56a6d2cc50..7ecb58e7e9 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -148,6 +148,7 @@ void InternTable::RemoveWeakFromTransaction(mirror::String* s) { } void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space) { + CHECK(image_space != nullptr); MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); if (!image_added_to_intern_table_) { mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 18de133147..b17f3039c6 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -138,7 +138,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s if (method->IsStatic()) { if (shorty == "L") { typedef jobject (fntype)(JNIEnv*, jclass); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); jobject jresult; @@ -149,35 +149,35 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetL(soa.Decode<Object*>(jresult)); } else if (shorty == "V") { typedef void (fntype)(JNIEnv*, jclass); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), klass.get()); } else if (shorty == "Z") { typedef jboolean (fntype)(JNIEnv*, jclass); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetZ(fn(soa.Env(), klass.get())); } else if (shorty == "BI") { typedef jbyte (fntype)(JNIEnv*, jclass, jint); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetB(fn(soa.Env(), klass.get(), args[0])); } else if (shorty == "II") { typedef jint (fntype)(JNIEnv*, jclass, jint); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetI(fn(soa.Env(), klass.get(), args[0])); } else if (shorty == "LL") { typedef jobject (fntype)(JNIEnv*, jclass, jobject); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg0(soa.Env(), @@ -190,14 +190,15 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetL(soa.Decode<Object*>(jresult)); } else if (shorty == "IIZ") { typedef jint (fntype)(JNIEnv*, jclass, jint, jboolean); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetI(fn(soa.Env(), klass.get(), args[0], args[1])); } else if (shorty == "ILI") { typedef jint (fntype)(JNIEnv*, jclass, jobject, jint); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>( + method->GetEntryPointFromJni())); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg0(soa.Env(), @@ -206,21 +207,21 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetI(fn(soa.Env(), klass.get(), arg0.get(), args[1])); } else if (shorty == "SIZ") { typedef jshort (fntype)(JNIEnv*, jclass, jint, jboolean); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetEntryPointFromJni())); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); result->SetS(fn(soa.Env(), klass.get(), args[0], args[1])); } else if (shorty == "VIZ") { typedef void (fntype)(JNIEnv*, jclass, jint, jboolean); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), klass.get(), args[0], args[1]); } else if (shorty == "ZLL") { typedef jboolean (fntype)(JNIEnv*, jclass, jobject, jobject); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg0(soa.Env(), @@ -231,7 +232,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetZ(fn(soa.Env(), klass.get(), arg0.get(), arg1.get())); } else if (shorty == "ZILL") { typedef jboolean (fntype)(JNIEnv*, jclass, jint, jobject, jobject); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg1(soa.Env(), @@ -242,7 +243,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetZ(fn(soa.Env(), klass.get(), args[0], arg1.get(), arg2.get())); } else if (shorty == "VILII") { typedef void (fntype)(JNIEnv*, jclass, jint, jobject, jint, jint); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg1(soa.Env(), @@ -251,7 +252,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s fn(soa.Env(), klass.get(), args[0], arg1.get(), args[2], args[3]); } else if (shorty == "VLILII") { typedef void (fntype)(JNIEnv*, jclass, jobject, jint, jobject, jint, jint); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jclass> klass(soa.Env(), soa.AddLocalReference<jclass>(method->GetDeclaringClass())); ScopedLocalRef<jobject> arg0(soa.Env(), @@ -267,7 +268,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s } else { if (shorty == "L") { typedef jobject (fntype)(JNIEnv*, jobject); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jobject> rcvr(soa.Env(), soa.AddLocalReference<jobject>(receiver)); jobject jresult; @@ -278,14 +279,14 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s result->SetL(soa.Decode<Object*>(jresult)); } else if (shorty == "V") { typedef void (fntype)(JNIEnv*, jobject); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jobject> rcvr(soa.Env(), soa.AddLocalReference<jobject>(receiver)); ScopedThreadStateChange tsc(self, kNative); fn(soa.Env(), rcvr.get()); } else if (shorty == "LL") { typedef jobject (fntype)(JNIEnv*, jobject, jobject); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jobject> rcvr(soa.Env(), soa.AddLocalReference<jobject>(receiver)); ScopedLocalRef<jobject> arg0(soa.Env(), @@ -299,7 +300,7 @@ static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& s ScopedThreadStateChange tsc(self, kNative); } else if (shorty == "III") { typedef jint (fntype)(JNIEnv*, jobject, jint, jint); - fntype* const fn = reinterpret_cast<fntype*>(const_cast<void*>(method->GetNativeMethod())); + fntype* const fn = reinterpret_cast<fntype*>(method->GetEntryPointFromJni()); ScopedLocalRef<jobject> rcvr(soa.Env(), soa.AddLocalReference<jobject>(receiver)); ScopedThreadStateChange tsc(self, kNative); diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 44f713ca96..1e0a2d2c22 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -125,6 +125,10 @@ struct ModBasket { }; static bool NeedsFullDeoptimization(JdwpEventKind eventKind) { + if (!Dbg::RequiresDeoptimization()) { + // We don't need deoptimization for debugging. + return false; + } switch (eventKind) { case EK_METHOD_ENTRY: case EK_METHOD_EXIT: diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 3b0656e4c6..1dcfcabf9d 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -360,7 +360,7 @@ class JNI { ScopedObjectAccess soa(env); mirror::ArtMethod* m = soa.DecodeMethod(mid); CHECK(!kMovingMethods); - jobject art_method = soa.AddLocalReference<jobject>(m); + ScopedLocalRef<jobject> art_method(env, soa.AddLocalReference<jobject>(m)); jobject reflect_method; if (m->IsConstructor()) { reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor); @@ -371,7 +371,7 @@ class JNI { return nullptr; } SetObjectField(env, reflect_method, - WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method); + WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, art_method.get()); return reflect_method; } @@ -379,13 +379,13 @@ class JNI { CHECK_NON_NULL_ARGUMENT(fid); ScopedObjectAccess soa(env); mirror::ArtField* f = soa.DecodeField(fid); - jobject art_field = soa.AddLocalReference<jobject>(f); + ScopedLocalRef<jobject> art_field(env, soa.AddLocalReference<jobject>(f)); jobject reflect_field = env->AllocObject(WellKnownClasses::java_lang_reflect_Field); if (env->ExceptionCheck()) { return nullptr; } SetObjectField(env, reflect_field, - WellKnownClasses::java_lang_reflect_Field_artField, art_field); + WellKnownClasses::java_lang_reflect_Field_artField, art_field.get()); return reflect_field; } diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index ccad137164..62b6b3407f 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -390,59 +390,72 @@ class JniInternalTest : public CommonCompilerTest { void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) { bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); CheckJniAbortCatcher jni_abort_catcher; - - jbooleanArray array = env_->NewBooleanArray(10); - ASSERT_TRUE(array != nullptr); - jboolean is_copy; - jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy); - ASSERT_TRUE(elements != nullptr); - env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array), - reinterpret_cast<jbyte*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected byte[]" - : "attempt to release byte primitive array elements with an object of type boolean[]"); - env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array), - reinterpret_cast<jshort*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected short[]" - : "attempt to release short primitive array elements with an object of type boolean[]"); - env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array), - reinterpret_cast<jchar*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected char[]" - : "attempt to release char primitive array elements with an object of type boolean[]"); - env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array), - reinterpret_cast<jint*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected int[]" - : "attempt to release int primitive array elements with an object of type boolean[]"); - env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), - reinterpret_cast<jlong*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected long[]" - : "attempt to release long primitive array elements with an object of type boolean[]"); - env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array), - reinterpret_cast<jfloat*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected float[]" - : "attempt to release float primitive array elements with an object of type boolean[]"); - env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), - reinterpret_cast<jdouble*>(elements), 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type boolean[] expected double[]" - : "attempt to release double primitive array elements with an object of type boolean[]"); - jbyteArray array2 = env_->NewByteArray(10); - env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0); - jni_abort_catcher.Check( - check_jni ? "incompatible array type byte[] expected boolean[]" - : "attempt to release boolean primitive array elements with an object of type byte[]"); - jobject object = env_->NewStringUTF("Test String"); - env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0); - jni_abort_catcher.Check( - check_jni ? "jarray argument has non-array type: java.lang.String" - : "attempt to release boolean primitive array elements with an object of type " + { + jbooleanArray array = env_->NewBooleanArray(10); + ASSERT_TRUE(array != nullptr); + jboolean is_copy; + jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy); + ASSERT_TRUE(elements != nullptr); + env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array), + reinterpret_cast<jbyte*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected byte[]" + : "attempt to release byte primitive array elements with an object of type boolean[]"); + env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array), + reinterpret_cast<jshort*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected short[]" + : "attempt to release short primitive array elements with an object of type boolean[]"); + env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array), + reinterpret_cast<jchar*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected char[]" + : "attempt to release char primitive array elements with an object of type boolean[]"); + env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array), + reinterpret_cast<jint*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected int[]" + : "attempt to release int primitive array elements with an object of type boolean[]"); + env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), + reinterpret_cast<jlong*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected long[]" + : "attempt to release long primitive array elements with an object of type boolean[]"); + env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array), + reinterpret_cast<jfloat*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected float[]" + : "attempt to release float primitive array elements with an object of type boolean[]"); + env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), + reinterpret_cast<jdouble*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected double[]" + : "attempt to release double primitive array elements with an object of type boolean[]"); + + // Don't leak the elements array. + env_->ReleaseBooleanArrayElements(array, elements, 0); + } + { + jbyteArray array = env_->NewByteArray(10); + jboolean is_copy; + jbyte* elements = env_->GetByteArrayElements(array, &is_copy); + + env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array), + reinterpret_cast<jboolean*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type byte[] expected boolean[]" + : "attempt to release boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), + reinterpret_cast<jboolean*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "jarray argument has non-array type: java.lang.String" + : "attempt to release boolean primitive array elements with an object of type " "java.lang.String"); + // Don't leak the elements array. + env_->ReleaseByteArrayElements(array, elements, 0); + } EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); } @@ -799,6 +812,11 @@ TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { ASSERT_NE(fid, nullptr); // Turn the fid into a java.lang.reflect.Field... jobject field = env_->ToReflectedField(c, fid, JNI_FALSE); + for (size_t i = 0; i <= kLocalsMax; ++i) { + // Regression test for b/18396311, ToReflectedField leaking local refs causing a local + // reference table overflows with 512 references to ArtField + env_->DeleteLocalRef(env_->ToReflectedField(c, fid, JNI_FALSE)); + } ASSERT_NE(c, nullptr); ASSERT_TRUE(env_->IsInstanceOf(field, jlrField)); // ...and back again. @@ -825,6 +843,11 @@ TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) { ASSERT_NE(mid, nullptr); // Turn the mid into a java.lang.reflect.Constructor... jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE); + for (size_t i = 0; i <= kLocalsMax; ++i) { + // Regression test for b/18396311, ToReflectedMethod leaking local refs causing a local + // reference table overflows with 512 references to ArtMethod + env_->DeleteLocalRef(env_->ToReflectedMethod(c, mid, JNI_FALSE)); + } ASSERT_NE(method, nullptr); ASSERT_TRUE(env_->IsInstanceOf(method, jlrConstructor)); // ...and back again. diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 494fa2fca2..b93651156e 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -199,17 +199,17 @@ inline void ArtMethod::SetPortableOatCodeOffset(uint32_t code_offset) { SetEntryPointFromPortableCompiledCode(reinterpret_cast<void*>(code_offset)); } -inline const uint8_t* ArtMethod::GetMappingTable() { - const void* code_pointer = GetQuickOatCodePointer(); +inline const uint8_t* ArtMethod::GetMappingTable(size_t pointer_size) { + const void* code_pointer = GetQuickOatCodePointer(pointer_size); if (code_pointer == nullptr) { return nullptr; } - return GetMappingTable(code_pointer); + return GetMappingTable(code_pointer, pointer_size); } -inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer) { +inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer, size_t pointer_size) { DCHECK(code_pointer != nullptr); - DCHECK(code_pointer == GetQuickOatCodePointer()); + DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size)); uint32_t offset = reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].mapping_table_offset_; if (UNLIKELY(offset == 0u)) { @@ -218,18 +218,18 @@ inline const uint8_t* ArtMethod::GetMappingTable(const void* code_pointer) { return reinterpret_cast<const uint8_t*>(code_pointer) - offset; } -inline const uint8_t* ArtMethod::GetVmapTable() { - const void* code_pointer = GetQuickOatCodePointer(); +inline const uint8_t* ArtMethod::GetVmapTable(size_t pointer_size) { + const void* code_pointer = GetQuickOatCodePointer(pointer_size); if (code_pointer == nullptr) { return nullptr; } - return GetVmapTable(code_pointer); + return GetVmapTable(code_pointer, pointer_size); } -inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) { - CHECK(!IsOptimized()) << "Unimplemented vmap table for optimized compiler"; +inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer, size_t pointer_size) { + CHECK(!IsOptimized(pointer_size)) << "Unimplemented vmap table for optimized compiler"; DCHECK(code_pointer != nullptr); - DCHECK(code_pointer == GetQuickOatCodePointer()); + DCHECK_EQ(code_pointer, GetQuickOatCodePointer(pointer_size)); uint32_t offset = reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_; if (UNLIKELY(offset == 0u)) { @@ -243,8 +243,8 @@ inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) { } inline CodeInfo ArtMethod::GetOptimizedCodeInfo() { - DCHECK(IsOptimized()); - const void* code_pointer = GetQuickOatCodePointer(); + DCHECK(IsOptimized(sizeof(void*))); + const void* code_pointer = GetQuickOatCodePointer(sizeof(void*)); DCHECK(code_pointer != nullptr); uint32_t offset = reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_; @@ -303,19 +303,14 @@ inline bool ArtMethod::IsImtUnimplementedMethod() { } inline uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc) { - const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this); + const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor( + this, sizeof(void*)); return pc - reinterpret_cast<uintptr_t>(code); } -template<VerifyObjectFlags kVerifyFlags> -inline void ArtMethod::SetNativeMethod(const void* native_method) { - SetFieldPtr<false, true, kVerifyFlags>( - OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_), native_method); -} - inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo(const void* code_pointer) { DCHECK(code_pointer != nullptr); - DCHECK_EQ(code_pointer, GetQuickOatCodePointer()); + DCHECK_EQ(code_pointer, GetQuickOatCodePointer(sizeof(void*))); return reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].frame_info_; } @@ -485,6 +480,12 @@ inline mirror::Class* ArtMethod::GetReturnType(bool resolve) { return type; } +inline void ArtMethod::CheckObjectSizeEqualsMirrorSize() { + // Using the default, check the class object size to make sure it matches the size of the + // object. + DCHECK_EQ(GetClass()->GetObjectSize(), sizeof(*this)); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index a742aaad9a..6d4af83058 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -165,9 +165,9 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) { // Portable doesn't use the machine pc, we just use dex pc instead. return static_cast<uint32_t>(pc); } - const void* entry_point = GetQuickOatEntryPoint(); - MappingTable table( - entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr); + const void* entry_point = GetQuickOatEntryPoint(sizeof(void*)); + MappingTable table(entry_point != nullptr ? + GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr); if (table.TotalSize() == 0) { // NOTE: Special methods (see Mir2Lir::GenSpecialCase()) have an empty mapping // but they have no suspend checks and, consequently, we never call ToDexPc() for them. @@ -198,9 +198,9 @@ uint32_t ArtMethod::ToDexPc(const uintptr_t pc, bool abort_on_failure) { } uintptr_t ArtMethod::ToNativeQuickPc(const uint32_t dex_pc, bool abort_on_failure) { - const void* entry_point = GetQuickOatEntryPoint(); - MappingTable table( - entry_point != nullptr ? GetMappingTable(EntryPointToCodePointer(entry_point)) : nullptr); + const void* entry_point = GetQuickOatEntryPoint(sizeof(void*)); + MappingTable table(entry_point != nullptr ? + GetMappingTable(EntryPointToCodePointer(entry_point), sizeof(void*)) : nullptr); if (table.TotalSize() == 0) { DCHECK_EQ(dex_pc, 0U); return 0; // Special no mapping/pc == 0 case @@ -320,13 +320,13 @@ bool ArtMethod::IsEntrypointInterpreter() { } } -const void* ArtMethod::GetQuickOatEntryPoint() { +const void* ArtMethod::GetQuickOatEntryPoint(size_t pointer_size) { if (IsPortableCompiled() || IsAbstract() || IsRuntimeMethod() || IsProxyMethod()) { return nullptr; } Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this); + const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this, pointer_size); // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) // for non-native methods. @@ -340,7 +340,7 @@ const void* ArtMethod::GetQuickOatEntryPoint() { #ifndef NDEBUG uintptr_t ArtMethod::NativeQuickPcOffset(const uintptr_t pc, const void* quick_entry_point) { CHECK_NE(quick_entry_point, GetQuickToInterpreterBridge()); - CHECK_EQ(quick_entry_point, Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this)); + CHECK_EQ(quick_entry_point, Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*))); return pc - reinterpret_cast<uintptr_t>(quick_entry_point); } #endif @@ -447,7 +447,7 @@ QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { return runtime->GetRuntimeMethodFrameInfo(this); } - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*)); ClassLinker* class_linker = runtime->GetClassLinker(); // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) @@ -483,7 +483,7 @@ void ArtMethod::RegisterNative(const void* native_method, bool is_fast) { if (is_fast) { SetAccessFlags(GetAccessFlags() | kAccFastNative); } - SetNativeMethod(native_method); + SetEntryPointFromJni(native_method); } void ArtMethod::UnregisterNative() { diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index d92d00a8cb..d292552af2 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -48,11 +48,6 @@ class MANAGED ArtMethod FINAL : public Object { // Size of java.lang.reflect.ArtMethod.class. static uint32_t ClassSize(); - // Size of an instance of java.lang.reflect.ArtMethod not including its value array. - static constexpr uint32_t InstanceSize() { - return sizeof(ArtMethod); - } - static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -151,13 +146,13 @@ class MANAGED ArtMethod FINAL : public Object { SetAccessFlags(GetAccessFlags() | kAccPreverified); } - bool IsOptimized() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool IsOptimized(size_t pointer_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Temporary solution for detecting if a method has been optimized: the compiler // does not create a GC map. Instead, the vmap table contains the stack map // (as in stack_map.h). - return (GetEntryPointFromQuickCompiledCode() != nullptr) - && (GetQuickOatCodePointer() != nullptr) - && (GetNativeGcMap() == nullptr); + return GetEntryPointFromQuickCompiledCodePtrSize(pointer_size) != nullptr + && GetQuickOatCodePointer(pointer_size) != nullptr + && GetNativeGcMapPtrSize(pointer_size) == nullptr; } bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -258,49 +253,92 @@ class MANAGED ArtMethod FINAL : public Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> EntryPointFromInterpreter* GetEntryPointFromInterpreter() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldPtr<EntryPointFromInterpreter*, kVerifyFlags>( - OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_)); + CheckObjectSizeEqualsMirrorSize(); + return GetEntryPointFromInterpreterPtrSize(sizeof(void*)); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + EntryPointFromInterpreter* GetEntryPointFromInterpreterPtrSize(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldPtrWithSize<EntryPointFromInterpreter*, kVerifyFlags>( + EntryPointFromInterpreterOffset(pointer_size), pointer_size); + } + + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> void SetEntryPointFromInterpreter(EntryPointFromInterpreter* entry_point_from_interpreter) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetFieldPtr<false, true, kVerifyFlags>( - OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_interpreter_), - entry_point_from_interpreter); + CheckObjectSizeEqualsMirrorSize(); + SetEntryPointFromInterpreterPtrSize(entry_point_from_interpreter, sizeof(void*)); + } + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void SetEntryPointFromInterpreterPtrSize(EntryPointFromInterpreter* entry_point_from_interpreter, + size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldPtrWithSize<false, true, kVerifyFlags>( + EntryPointFromInterpreterOffset(pointer_size), entry_point_from_interpreter, pointer_size); } - static MemberOffset EntryPointFromPortableCompiledCodeOffset() { - return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_portable_compiled_code_)); + ALWAYS_INLINE static MemberOffset EntryPointFromPortableCompiledCodeOffset(size_t pointer_size) { + return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER( + PtrSizedFields, entry_point_from_portable_compiled_code_) / sizeof(void*) * pointer_size); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - const void* GetEntryPointFromPortableCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldPtr<const void*, kVerifyFlags>( - EntryPointFromPortableCompiledCodeOffset()); + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + const void* GetEntryPointFromPortableCompiledCode() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CheckObjectSizeEqualsMirrorSize(); + return GetEntryPointFromPortableCompiledCodePtrSize(sizeof(void*)); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE const void* GetEntryPointFromPortableCompiledCodePtrSize(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldPtrWithSize<const void*, kVerifyFlags>( + EntryPointFromPortableCompiledCodeOffset(pointer_size), pointer_size); + } + + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> void SetEntryPointFromPortableCompiledCode(const void* entry_point_from_portable_compiled_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetFieldPtr<false, true, kVerifyFlags>( - EntryPointFromPortableCompiledCodeOffset(), entry_point_from_portable_compiled_code); + CheckObjectSizeEqualsMirrorSize(); + return SetEntryPointFromPortableCompiledCodePtrSize(entry_point_from_portable_compiled_code, + sizeof(void*)); } - static MemberOffset EntryPointFromQuickCompiledCodeOffset() { - return MemberOffset(OFFSETOF_MEMBER(ArtMethod, entry_point_from_quick_compiled_code_)); + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void SetEntryPointFromPortableCompiledCodePtrSize( + const void* entry_point_from_portable_compiled_code, size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldPtrWithSize<false, true, kVerifyFlags>( + EntryPointFromPortableCompiledCodeOffset(pointer_size), + entry_point_from_portable_compiled_code, pointer_size); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> const void* GetEntryPointFromQuickCompiledCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldPtr<const void*, kVerifyFlags>(EntryPointFromQuickCompiledCodeOffset()); + CheckObjectSizeEqualsMirrorSize(); + return GetEntryPointFromQuickCompiledCodePtrSize(sizeof(void*)); + } + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE const void* GetEntryPointFromQuickCompiledCodePtrSize(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldPtrWithSize<const void*, kVerifyFlags>( + EntryPointFromQuickCompiledCodeOffset(pointer_size), pointer_size); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetFieldPtr<false, true, kVerifyFlags>( - EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code); + CheckObjectSizeEqualsMirrorSize(); + SetEntryPointFromQuickCompiledCodePtrSize(entry_point_from_quick_compiled_code, + sizeof(void*)); + } + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize( + const void* entry_point_from_quick_compiled_code, size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldPtrWithSize<false, true, kVerifyFlags>( + EntryPointFromQuickCompiledCodeOffset(pointer_size), entry_point_from_quick_compiled_code, + pointer_size); } uint32_t GetCodeSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -343,33 +381,48 @@ class MANAGED ArtMethod FINAL : public Object { return reinterpret_cast<const void*>(code); } - // Actual entry point pointer to compiled oat code or nullptr if method has no compiled code. - const void* GetQuickOatEntryPoint() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - + // Actual entry point pointer to compiled oat code or nullptr. + const void* GetQuickOatEntryPoint(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Actual pointer to compiled oat code or nullptr. - const void* GetQuickOatCodePointer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return EntryPointToCodePointer(GetQuickOatEntryPoint()); + const void* GetQuickOatCodePointer(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return EntryPointToCodePointer(GetQuickOatEntryPoint(pointer_size)); } // Callers should wrap the uint8_t* in a MappingTable instance for convenient access. - const uint8_t* GetMappingTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const uint8_t* GetMappingTable(const void* code_pointer) + const uint8_t* GetMappingTable(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const uint8_t* GetMappingTable(const void* code_pointer, size_t pointer_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Callers should wrap the uint8_t* in a VmapTable instance for convenient access. - const uint8_t* GetVmapTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const uint8_t* GetVmapTable(const void* code_pointer) + const uint8_t* GetVmapTable(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const uint8_t* GetVmapTable(const void* code_pointer, size_t pointer_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); CodeInfo GetOptimizedCodeInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_)); + CheckObjectSizeEqualsMirrorSize(); + return GetNativeGcMapPtrSize(sizeof(void*)); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE const uint8_t* GetNativeGcMapPtrSize(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldPtrWithSize<uint8_t*>(GcMapOffset(pointer_size), pointer_size); + } + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> void SetNativeGcMap(const uint8_t* data) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - SetFieldPtr<false, true, kVerifyFlags>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_), data); + CheckObjectSizeEqualsMirrorSize(); + SetNativeGcMapPtrSize(data, sizeof(void*)); + } + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE void SetNativeGcMapPtrSize(const uint8_t* data, size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldPtrWithSize<false, true, kVerifyFlags>(GcMapOffset(pointer_size), data, + pointer_size); } // When building the oat need a convenient place to stuff the offset of the native GC map. @@ -409,16 +462,46 @@ class MANAGED ArtMethod FINAL : public Object { void UnregisterNative() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static MemberOffset NativeMethodOffset() { - return OFFSET_OF_OBJECT_MEMBER(ArtMethod, entry_point_from_jni_); + static MemberOffset EntryPointFromInterpreterOffset(size_t pointer_size) { + return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER( + PtrSizedFields, entry_point_from_interpreter_) / sizeof(void*) * pointer_size); } - const void* GetNativeMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldPtr<const void*>(NativeMethodOffset()); + static MemberOffset EntryPointFromJniOffset(size_t pointer_size) { + return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER( + PtrSizedFields, entry_point_from_jni_) / sizeof(void*) * pointer_size); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - void SetNativeMethod(const void*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static MemberOffset EntryPointFromQuickCompiledCodeOffset(size_t pointer_size) { + return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER( + PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size); + } + + static MemberOffset GcMapOffset(size_t pointer_size) { + return MemberOffset(PtrSizedFieldsOffset() + OFFSETOF_MEMBER( + PtrSizedFields, gc_map_) / sizeof(void*) * pointer_size); + } + + void* GetEntryPointFromJni() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CheckObjectSizeEqualsMirrorSize(); + return GetEntryPointFromJniPtrSize(sizeof(void*)); + } + ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldPtrWithSize<void*>(EntryPointFromJniOffset(pointer_size), pointer_size); + } + + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + void SetEntryPointFromJni(const void* entrypoint) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CheckObjectSizeEqualsMirrorSize(); + SetEntryPointFromJniPtrSize<kVerifyFlags>(entrypoint, sizeof(void*)); + } + template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldPtrWithSize<false, true, kVerifyFlags>( + EntryPointFromJniOffset(pointer_size), entrypoint, pointer_size); + } static MemberOffset GetMethodIndexOffset() { return OFFSET_OF_OBJECT_MEMBER(ArtMethod, method_index_); @@ -521,7 +604,16 @@ class MANAGED ArtMethod FINAL : public Object { ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - private: + static size_t SizeWithoutPointerFields() { + return sizeof(ArtMethod) - sizeof(PtrSizedFields); + } + + // Size of an instance of java.lang.reflect.ArtMethod not including its value array. + static size_t InstanceSize(size_t pointer_size) { + return SizeWithoutPointerFields() + (sizeof(PtrSizedFields) / sizeof(void*)) * pointer_size; + } + + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. HeapReference<Class> declaring_class_; @@ -535,26 +627,6 @@ class MANAGED ArtMethod FINAL : public Object { // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access. HeapReference<ObjectArray<String>> dex_cache_strings_; - // Method dispatch from the interpreter invokes this pointer which may cause a bridge into - // compiled code. - uint64_t entry_point_from_interpreter_; - - // Pointer to JNI function registered to this method, or a function to resolve the JNI function. - uint64_t entry_point_from_jni_; - - // Method dispatch from portable compiled code invokes this pointer which may cause bridging into - // quick compiled code or the interpreter. - uint64_t entry_point_from_portable_compiled_code_; - - // Method dispatch from quick compiled code invokes this pointer which may cause bridging into - // portable compiled code or the interpreter. - uint64_t entry_point_from_quick_compiled_code_; - - // Pointer to a data structure created by the compiler and used by the garbage collector to - // determine which registers hold live references to objects within the heap. Keyed by native PC - // offsets for the quick compiler and dex PCs for the portable. - uint64_t gc_map_; - // Access flags; low 16 bits are defined by spec. uint32_t access_flags_; @@ -573,15 +645,46 @@ class MANAGED ArtMethod FINAL : public Object { // ifTable. uint32_t method_index_; + // Add alignment word here if necessary. + + // Must be the last fields in the method. + struct PACKED(4) PtrSizedFields { + // Method dispatch from the interpreter invokes this pointer which may cause a bridge into + // compiled code. + void* entry_point_from_interpreter_; + + // Pointer to JNI function registered to this method, or a function to resolve the JNI function. + void* entry_point_from_jni_; + + // Method dispatch from quick compiled code invokes this pointer which may cause bridging into + // portable compiled code or the interpreter. + void* entry_point_from_quick_compiled_code_; + + // Pointer to a data structure created by the compiler and used by the garbage collector to + // determine which registers hold live references to objects within the heap. Keyed by native PC + // offsets for the quick compiler and dex PCs for the portable. + void* gc_map_; + + // Method dispatch from portable compiled code invokes this pointer which may cause bridging + // into quick compiled code or the interpreter. Last to simplify entrypoint logic. + void* entry_point_from_portable_compiled_code_; + } ptr_sized_fields_; + static GcRoot<Class> java_lang_reflect_ArtMethod_; private: + ALWAYS_INLINE void CheckObjectSizeEqualsMirrorSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ALWAYS_INLINE ObjectArray<ArtMethod>* GetDexCacheResolvedMethods() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); ALWAYS_INLINE ObjectArray<Class>* GetDexCacheResolvedTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static size_t PtrSizedFieldsOffset() { + return OFFSETOF_MEMBER(ArtMethod, ptr_sized_fields_); + } + friend struct art::ArtMethodOffsets; // for verifying offset information DISALLOW_IMPLICIT_CONSTRUCTORS(ArtMethod); }; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index f45ea8571f..82425b559d 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -528,6 +528,13 @@ class MANAGED Class FINAL : public Object { return SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size); } + void SetObjectSizeWithoutChecks(uint32_t new_object_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Not called within a transaction. + return SetField32<false, false, kVerifyNone>( + OFFSET_OF_OBJECT_MEMBER(Class, object_size_), new_object_size); + } + // Returns true if this class is in the same packages as that class. bool IsInSamePackage(Class* that) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 4199eef619..121947ddee 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -404,8 +404,7 @@ inline size_t Object::SizeOf() { } DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass<kNewFlags, kReadBarrierOption>()); - DCHECK(!(IsArtField<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtField)); - DCHECK(!(IsArtMethod<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtMethod)); + DCHECK(!(IsArtField<kNewFlags, kReadBarrierOption>()) || result == sizeof(ArtField)); return result; } @@ -962,7 +961,6 @@ inline void Object::VisitReferences(const Visitor& visitor, } } } - } // namespace mirror } // namespace art diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 0ce5231b40..221feca6ad 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -392,15 +392,26 @@ class MANAGED LOCKABLE Object { VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T> void SetFieldPtr(MemberOffset field_offset, T new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifndef __LP64__ - SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>( - field_offset, reinterpret_cast<int32_t>(new_value)); -#else - SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>( - field_offset, reinterpret_cast<int64_t>(new_value)); -#endif + SetFieldPtrWithSize<kTransactionActive, kCheckTransaction, kVerifyFlags>( + field_offset, new_value, sizeof(void*)); } + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T> + ALWAYS_INLINE void SetFieldPtrWithSize(MemberOffset field_offset, T new_value, + size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size; + if (pointer_size == 4) { + intptr_t ptr = reinterpret_cast<intptr_t>(new_value); + DCHECK_EQ(static_cast<int32_t>(ptr), ptr); // Check that we dont lose any non 0 bits. + SetField32<kTransactionActive, kCheckTransaction, kVerifyFlags>( + field_offset, static_cast<int32_t>(ptr)); + } else { + SetField64<kTransactionActive, kCheckTransaction, kVerifyFlags>( + field_offset, static_cast<int64_t>(reinterpret_cast<intptr_t>(new_value))); + } + } // TODO fix thread safety analysis broken by the use of template. This should be // SHARED_LOCKS_REQUIRED(Locks::mutator_lock_). template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, @@ -413,11 +424,21 @@ class MANAGED LOCKABLE Object { template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> T GetFieldPtr(MemberOffset field_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifndef __LP64__ - return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset)); -#else - return reinterpret_cast<T>(GetField64<kVerifyFlags, kIsVolatile>(field_offset)); -#endif + return GetFieldPtrWithSize<T, kVerifyFlags, kIsVolatile>(field_offset, sizeof(void*)); + } + + template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> + ALWAYS_INLINE T GetFieldPtrWithSize(MemberOffset field_offset, size_t pointer_size) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(pointer_size == 4 || pointer_size == 8) << pointer_size; + if (pointer_size == 4) { + return reinterpret_cast<T>(GetField32<kVerifyFlags, kIsVolatile>(field_offset)); + } else { + int64_t v = GetField64<kVerifyFlags, kIsVolatile>(field_offset); + // Check that we dont lose any non 0 bits. + DCHECK_EQ(reinterpret_cast<int64_t>(reinterpret_cast<T>(v)), v); + return reinterpret_cast<T>(v); + } } // TODO: Fixme when anotatalysis works with visitors. diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index a0aaa9e8c7..4402031ef1 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -78,6 +78,14 @@ class ObjectTest : public CommonRuntimeTest { TEST_F(ObjectTest, Constants) { EXPECT_EQ(kObjectReferenceSize, sizeof(HeapReference<Object>)); EXPECT_EQ(kObjectHeaderSize, sizeof(Object)); + EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_32, + ArtMethod::EntryPointFromPortableCompiledCodeOffset(4).Int32Value()); + EXPECT_EQ(MIRROR_ART_METHOD_PORTABLE_CODE_OFFSET_64, + ArtMethod::EntryPointFromPortableCompiledCodeOffset(8).Int32Value()); + EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_32, + ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value()); + EXPECT_EQ(MIRROR_ART_METHOD_QUICK_CODE_OFFSET_64, + ArtMethod::EntryPointFromQuickCompiledCodeOffset(8).Int32Value()); } TEST_F(ObjectTest, IsInSamePackage) { diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 64408a6604..30b8aa3c97 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -109,6 +109,14 @@ class MANAGED String FINAL : public Object { int32_t CompareTo(String* other) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Offset is only used during testing so use non-transactional mode. + DCHECK_LE(0, new_offset); + SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset); + } + + void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static Class* GetJavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(!java_lang_String_.IsNull()); return java_lang_String_.Read(); @@ -134,21 +142,12 @@ class MANAGED String FINAL : public Object { SetField32<false, false>(OFFSET_OF_OBJECT_MEMBER(String, count_), new_count); } - void SetOffset(int32_t new_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Offset is only used during testing so use non-transactional mode. - DCHECK_LE(0, new_offset); - DCHECK_GE(GetLength(), new_offset); - SetField32<false>(OFFSET_OF_OBJECT_MEMBER(String, offset_), new_offset); - } - static String* Alloc(Thread* self, int32_t utf16_length) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static String* Alloc(Thread* self, Handle<CharArray> array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void SetArray(CharArray* new_array) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". HeapReference<CharArray> array_; diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index c2c6b12037..ffadfc61a7 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -72,7 +72,7 @@ static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* met if (count < method_count) { methods[count].name = m->GetName(); methods[count].signature = m->GetShorty(); - methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod()); + methods[count].fnPtr = m->GetEntryPointFromJni(); count++; } else { LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m); @@ -85,7 +85,7 @@ static uint32_t GetNativeMethods(JNIEnv* env, jclass clazz, JNINativeMethod* met if (count < method_count) { methods[count].name = m->GetName(); methods[count].signature = m->GetShorty(); - methods[count].fnPtr = const_cast<void*>(m->GetNativeMethod()); + methods[count].fnPtr = m->GetEntryPointFromJni(); count++; } else { LOG(WARNING) << "Output native method array too small. Skipping " << PrettyMethod(m); diff --git a/runtime/oat.cc b/runtime/oat.cc index 2f7357fa7f..bfb27ddb3c 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -25,7 +25,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '4', '7', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '4', '8', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) { size_t estimate = 0U; diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 1a97c357fa..3e6c86b7c9 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -105,11 +105,7 @@ ParsedOptions::ParsedOptions() profile_clock_source_(kDefaultTraceClockSource), verify_(true), image_isa_(kRuntimeISA), - use_homogeneous_space_compaction_for_oom_(false), // If we are using homogeneous space - // compaction then default background - // compaction to off since homogeneous - // space compactions when we transition - // to not jank perceptible. + use_homogeneous_space_compaction_for_oom_(true), // Enable hspace compaction on OOM by default. min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000)) // 100s. {} diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 78c6542827..078e7d24e1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -438,10 +438,14 @@ bool Runtime::Start() { started_ = true; - if (IsZygote()) { + // Use !IsCompiler so that we get test coverage, tests are never the zygote. + if (!IsCompiler()) { ScopedObjectAccess soa(self); - Runtime::Current()->GetInternTable()->AddImageStringsToTable(heap_->GetImageSpace()); - Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable(); + gc::space::ImageSpace* image_space = heap_->GetImageSpace(); + if (image_space != nullptr) { + Runtime::Current()->GetInternTable()->AddImageStringsToTable(image_space); + Runtime::Current()->GetClassLinker()->MoveImageClassesToClassTable(); + } } if (!IsImageDex2OatEnabled() || !Runtime::Current()->GetHeap()->HasImageSpace()) { diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index d448460dc3..e377542e45 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -110,11 +110,17 @@ void SignalCatcher::Output(const std::string& s) { PLOG(ERROR) << "Unable to open stack trace file '" << stack_trace_file_ << "'"; return; } - std::unique_ptr<File> file(new File(fd, stack_trace_file_)); - if (!file->WriteFully(s.data(), s.size())) { - PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'"; + std::unique_ptr<File> file(new File(fd, stack_trace_file_, true)); + bool success = file->WriteFully(s.data(), s.size()); + if (success) { + success = file->FlushCloseOrErase() == 0; } else { + file->Erase(); + } + if (success) { LOG(INFO) << "Wrote stack traces to '" << stack_trace_file_ << "'"; + } else { + PLOG(ERROR) << "Failed to write stack traces to '" << stack_trace_file_ << "'"; } } diff --git a/runtime/stack.cc b/runtime/stack.cc index 44086096f0..43714b95e8 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -125,7 +125,7 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } - } else if (m->IsOptimized()) { + } else if (m->IsOptimized(sizeof(void*))) { // TODO: Implement, currently only used for exceptions when jdwp is enabled. UNIMPLEMENTED(WARNING) << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler"; @@ -153,9 +153,9 @@ bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably read registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(); + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer)); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); uint32_t vmap_offset; // TODO: IsInContext stops before spotting floating point registers. @@ -207,9 +207,9 @@ bool StackVisitor::GetVRegPair(mirror::ArtMethod* m, uint16_t vreg, VRegKind kin if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably read registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(); + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer)); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); uint32_t vmap_offset_lo, vmap_offset_hi; // TODO: IsInContext stops before spotting floating point registers. @@ -254,9 +254,9 @@ bool StackVisitor::SetVReg(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_val if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably write registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(); + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer)); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); uint32_t vmap_offset; // TODO: IsInContext stops before spotting floating point registers. @@ -318,9 +318,9 @@ bool StackVisitor::SetVRegPair(mirror::ArtMethod* m, uint16_t vreg, uint64_t new if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably write registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(); + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer)); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); uint32_t vmap_offset_lo, vmap_offset_hi; // TODO: IsInContext stops before spotting floating point registers. diff --git a/runtime/thread.cc b/runtime/thread.cc index c769faf3e5..cd47b5ea6e 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2136,9 +2136,9 @@ class ReferenceMapVisitor : public StackVisitor { // Process register map (which native and runtime methods don't have) if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) { - if (m->IsOptimized()) { + if (m->IsOptimized(sizeof(void*))) { Runtime* runtime = Runtime::Current(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*)); uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point); StackMap map = m->GetStackMap(native_pc_offset); MemoryRegion mask = map.GetStackMask(); @@ -2167,12 +2167,12 @@ class ReferenceMapVisitor : public StackVisitor { static_cast<size_t>(code_item->registers_size_)); if (num_regs > 0) { Runtime* runtime = Runtime::Current(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(m, sizeof(void*)); uintptr_t native_pc_offset = m->NativeQuickPcOffset(GetCurrentQuickFramePc(), entry_point); const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset); DCHECK(reg_bitmap != nullptr); const void* code_pointer = mirror::ArtMethod::EntryPointToCodePointer(entry_point); - const VmapTable vmap_table(m->GetVmapTable(code_pointer)); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); // For all dex registers in the bitmap DCHECK(cur_quick_frame != nullptr); diff --git a/runtime/trace.cc b/runtime/trace.cc index 29c01e4d47..2cc50b3732 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -431,6 +431,15 @@ void Trace::Stop() { instrumentation::Instrumentation::kMethodExited | instrumentation::Instrumentation::kMethodUnwind); } + if (the_trace->trace_file_.get() != nullptr) { + // Do not try to erase, so flush and close explicitly. + if (the_trace->trace_file_->Flush() != 0) { + PLOG(ERROR) << "Could not flush trace file."; + } + if (the_trace->trace_file_->Close() != 0) { + PLOG(ERROR) << "Could not close trace file."; + } + } delete the_trace; } runtime->GetThreadList()->ResumeAll(); diff --git a/runtime/utils.cc b/runtime/utils.cc index 9a4c8759b8..ad46be644f 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1120,13 +1120,20 @@ std::string GetSchedulerGroupName(pid_t tid) { void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix, mirror::ArtMethod* current_method) { - // TODO: enable on __linux__ b/15446488. -#if 0 +#if __linux__ // b/18119146 if (RUNNING_ON_VALGRIND != 0) { return; } +#if !defined(HAVE_ANDROID_OS) + if (GetTid() != tid) { + // TODO: dumping of other threads is disabled to avoid crashes during stress testing. + // b/15446488. + return; + } +#endif + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid)); if (!backtrace->Unwind(0)) { os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n"; diff --git a/runtime/utils.h b/runtime/utils.h index d83013abec..668c897276 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -165,6 +165,18 @@ struct TypeIdentity { typedef T type; }; +// Like sizeof, but count how many bits a type takes. Pass type explicitly. +template <typename T> +static constexpr size_t BitSizeOf() { + return sizeof(T) * CHAR_BIT; +} + +// Like sizeof, but count how many bits a type takes. Infers type from parameter. +template <typename T> +static constexpr size_t BitSizeOf(T /*x*/) { + return sizeof(T) * CHAR_BIT; +} + // For rounding integers. template<typename T> static constexpr T RoundDown(T x, typename TypeIdentity<T>::type n) WARN_UNUSED; @@ -201,10 +213,39 @@ static inline T* AlignUp(T* x, uintptr_t n) { return reinterpret_cast<T*>(RoundUp(reinterpret_cast<uintptr_t>(x), n)); } +namespace utils { +namespace detail { // Private, implementation-specific namespace. Do not poke outside of this file. +template <typename T> +static constexpr inline T RoundUpToPowerOfTwoRecursive(T x, size_t bit) { + return bit == (BitSizeOf<T>()) ? x: RoundUpToPowerOfTwoRecursive(x | x >> bit, bit << 1); +} +} // namespace detail +} // namespace utils + +// Recursive implementation is from "Hacker's Delight" by Henry S. Warren, Jr., +// figure 3-3, page 48, where the function is called clp2. +template <typename T> +static constexpr inline T RoundUpToPowerOfTwo(T x) { + return art::utils::detail::RoundUpToPowerOfTwoRecursive(x - 1, 1) + 1; +} + +// Find the bit position of the most significant bit (0-based), or -1 if there were no bits set. +template <typename T> +static constexpr ssize_t MostSignificantBit(T value) { + return (value == 0) ? -1 : (MostSignificantBit(value >> 1) + 1); +} + +// How many bits (minimally) does it take to store the constant 'value'? i.e. 1 for 1, 3 for 5, etc. +template <typename T> +static constexpr size_t MinimumBitsToStore(T value) { + return static_cast<size_t>(MostSignificantBit(value) + 1); +} + template<typename T> static constexpr int CLZ(T x) { + static_assert(sizeof(T) <= sizeof(long long), "T too large, must be smaller than long long"); // NOLINT [runtime/int] [4] return (sizeof(T) == sizeof(uint32_t)) - ? __builtin_clz(x) + ? __builtin_clz(x) // TODO: __builtin_clz[ll] has undefined behavior for x=0 : __builtin_clzll(x); } diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 92323da554..a98bc909f2 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -402,4 +402,36 @@ TEST_F(UtilsTest, ExecError) { } } +TEST_F(UtilsTest, RoundUpToPowerOfTwo) { + // Tests the constexpr variant since all the parameters are constexpr + EXPECT_EQ(0, RoundUpToPowerOfTwo(0)); + EXPECT_EQ(1, RoundUpToPowerOfTwo(1)); + EXPECT_EQ(2, RoundUpToPowerOfTwo(2)); + EXPECT_EQ(4, RoundUpToPowerOfTwo(3)); + EXPECT_EQ(8, RoundUpToPowerOfTwo(7)); + + EXPECT_EQ(0b10000L, RoundUpToPowerOfTwo(0b01101L)); + EXPECT_EQ(1ULL << 63, RoundUpToPowerOfTwo(1ULL << 62 | 1ULL)); +} + +TEST_F(UtilsTest, MostSignificantBit) { + EXPECT_EQ(-1, MostSignificantBit(0)); + EXPECT_EQ(0, MostSignificantBit(1)); + EXPECT_EQ(31, MostSignificantBit(~static_cast<uint32_t>(0))); + EXPECT_EQ(2, MostSignificantBit(0b110)); + EXPECT_EQ(2, MostSignificantBit(0b100)); +} + +TEST_F(UtilsTest, MinimumBitsToStore) { + EXPECT_EQ(0u, MinimumBitsToStore(0)); + EXPECT_EQ(1u, MinimumBitsToStore(1)); + EXPECT_EQ(2u, MinimumBitsToStore(0b10)); + EXPECT_EQ(2u, MinimumBitsToStore(0b11)); + EXPECT_EQ(3u, MinimumBitsToStore(0b100)); + EXPECT_EQ(3u, MinimumBitsToStore(0b110)); + EXPECT_EQ(3u, MinimumBitsToStore(0b101)); + EXPECT_EQ(8u, MinimumBitsToStore(0xFF)); + EXPECT_EQ(32u, MinimumBitsToStore(~static_cast<uint32_t>(0))); +} + } // namespace art diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc index 96abee2dc3..70a4ddaabf 100644 --- a/runtime/zip_archive_test.cc +++ b/runtime/zip_archive_test.cc @@ -41,7 +41,7 @@ TEST_F(ZipArchiveTest, FindAndExtract) { ScratchFile tmp; ASSERT_NE(-1, tmp.GetFd()); - std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename())); + std::unique_ptr<File> file(new File(tmp.GetFd(), tmp.GetFilename(), false)); ASSERT_TRUE(file.get() != NULL); bool success = zip_entry->ExtractToFile(*file, &error_msg); ASSERT_TRUE(success) << error_msg; diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt index 51bf8471c5..78c92fc51a 100644 --- a/test/083-compiler-regressions/expected.txt +++ b/test/083-compiler-regressions/expected.txt @@ -1,3 +1,4 @@ +b17325447 passes b17630605 passes b17411468 passes b2296099 passes diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java index 9ad8ea7b1b..285c3608ca 100644 --- a/test/083-compiler-regressions/src/Main.java +++ b/test/083-compiler-regressions/src/Main.java @@ -30,6 +30,7 @@ public class Main { } public static void main(String args[]) throws Exception { + b17325447(); b17630605(); b17411468(); b2296099Test(); @@ -64,6 +65,31 @@ public class Main { minDoubleWith3ConstsTest(); } + public static double b17325447_i1(int i1, double f) { + return f; + } + + public static double b17325447_i2(int i1, int i2, double f) { + return f; + } + + public static double b17325447_i3(int i1, int i2, int i3, double f) { + return f; + } + + public static void b17325447() { + // b/17325447 - x86 handling of special identity method w/ double spanning reg/mem. + double d = 0.0; + d += b17325447_i1(123, 1.0); + d += b17325447_i2(123, 456, 2.0); + d += b17325447_i3(123, 456, 789, 3.0); + if (d == 6.0) { + System.out.println("b17325447 passes"); + } else { + System.out.println("b17325447 fails: " + d); + } + } + public static void b17630605() { // b/17630605 - failure to properly handle min long immediates. long a1 = 40455547223404749L; diff --git a/test/417-optimizing-arith-div/src/Main.java b/test/417-optimizing-arith-div/src/Main.java index a5dea15559..909ceb43d6 100644 --- a/test/417-optimizing-arith-div/src/Main.java +++ b/test/417-optimizing-arith-div/src/Main.java @@ -24,6 +24,12 @@ public class Main { } } + public static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + public static void expectEquals(float expected, float result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index 88b45280ac..37bc7779bd 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -24,6 +24,12 @@ public class Main { } } + public static void assertShortEquals(short expected, short result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + public static void assertIntEquals(int expected, int result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -39,23 +45,57 @@ public class Main { public static void assertCharEquals(char expected, char result) { if (expected != result) { // Values are cast to int to display numeric values instead of - // (Unicode) characters. + // (UTF-16 encoded) characters. throw new Error("Expected: " + (int)expected + ", found: " + (int)result); } } + public static void assertFloatEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertDoubleEquals(double expected, double result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void main(String[] args) { + // Generate, compile and check int-to-long Dex instructions. byteToLong(); shortToLong(); intToLong(); charToLong(); + // Generate, compile and check int-to-float Dex instructions. + byteToFloat(); + shortToFloat(); + intToFloat(); + charToFloat(); + + // Generate, compile and check int-to-double Dex instructions. + byteToDouble(); + shortToDouble(); + intToDouble(); + charToDouble(); + + // Generate, compile and check long-to-int Dex instructions. longToInt(); + // Generate, compile and check int-to-byte Dex instructions. shortToByte(); intToByte(); charToByte(); + // Generate, compile and check int-to-short Dex instructions. + byteToShort(); + intToShort(); + charToShort(); + + // Generate, compile and check int-to-char Dex instructions. byteToChar(); shortToChar(); intToChar(); @@ -100,16 +140,106 @@ public class Main { assertLongEquals(51L, $opt$CharToLong((char)51)); assertLongEquals(32767L, $opt$CharToLong((char)32767)); // 2^15 - 1 assertLongEquals(65535L, $opt$CharToLong((char)65535)); // 2^16 - 1 - - assertLongEquals(0L, $opt$CharToLong('\u0000')); - assertLongEquals(65535L, $opt$CharToLong('\uFFFF')); // 2^16 - 1 - assertLongEquals(65535L, $opt$CharToLong((char)-1)); assertLongEquals(65485L, $opt$CharToLong((char)-51)); assertLongEquals(32769L, $opt$CharToLong((char)-32767)); // -(2^15 - 1) assertLongEquals(32768L, $opt$CharToLong((char)-32768)); // -(2^15) } + private static void byteToFloat() { + assertFloatEquals(1F, $opt$ByteToFloat((byte)1)); + assertFloatEquals(0F, $opt$ByteToFloat((byte)0)); + assertFloatEquals(-1F, $opt$ByteToFloat((byte)-1)); + assertFloatEquals(51F, $opt$ByteToFloat((byte)51)); + assertFloatEquals(-51F, $opt$ByteToFloat((byte)-51)); + assertFloatEquals(127F, $opt$ByteToFloat((byte)127)); // 2^7 - 1 + assertFloatEquals(-127F, $opt$ByteToFloat((byte)-127)); // -(2^7 - 1) + assertFloatEquals(-128F, $opt$ByteToFloat((byte)-128)); // -(2^7) + } + + private static void shortToFloat() { + assertFloatEquals(1F, $opt$ShortToFloat((short)1)); + assertFloatEquals(0F, $opt$ShortToFloat((short)0)); + assertFloatEquals(-1F, $opt$ShortToFloat((short)-1)); + assertFloatEquals(51F, $opt$ShortToFloat((short)51)); + assertFloatEquals(-51F, $opt$ShortToFloat((short)-51)); + assertFloatEquals(32767F, $opt$ShortToFloat((short)32767)); // 2^15 - 1 + assertFloatEquals(-32767F, $opt$ShortToFloat((short)-32767)); // -(2^15 - 1) + assertFloatEquals(-32768F, $opt$ShortToFloat((short)-32768)); // -(2^15) + } + + private static void intToFloat() { + assertFloatEquals(1F, $opt$IntToFloat(1)); + assertFloatEquals(0F, $opt$IntToFloat(0)); + assertFloatEquals(-1F, $opt$IntToFloat(-1)); + assertFloatEquals(51F, $opt$IntToFloat(51)); + assertFloatEquals(-51F, $opt$IntToFloat(-51)); + assertFloatEquals(16777215F, $opt$IntToFloat(16777215)); // 2^24 - 1 + assertFloatEquals(-16777215F, $opt$IntToFloat(-16777215)); // -(2^24 - 1) + assertFloatEquals(16777216F, $opt$IntToFloat(16777216)); // 2^24 + assertFloatEquals(-16777216F, $opt$IntToFloat(-16777216)); // -(2^24) + assertFloatEquals(2147483647F, $opt$IntToFloat(2147483647)); // 2^31 - 1 + assertFloatEquals(-2147483648F, $opt$IntToFloat(-2147483648)); // -(2^31) + } + + private static void charToFloat() { + assertFloatEquals(1F, $opt$CharToFloat((char)1)); + assertFloatEquals(0F, $opt$CharToFloat((char)0)); + assertFloatEquals(51F, $opt$CharToFloat((char)51)); + assertFloatEquals(32767F, $opt$CharToFloat((char)32767)); // 2^15 - 1 + assertFloatEquals(65535F, $opt$CharToFloat((char)65535)); // 2^16 - 1 + assertFloatEquals(65535F, $opt$CharToFloat((char)-1)); + assertFloatEquals(65485F, $opt$CharToFloat((char)-51)); + assertFloatEquals(32769F, $opt$CharToFloat((char)-32767)); // -(2^15 - 1) + assertFloatEquals(32768F, $opt$CharToFloat((char)-32768)); // -(2^15) + } + + private static void byteToDouble() { + assertDoubleEquals(1D, $opt$ByteToDouble((byte)1)); + assertDoubleEquals(0D, $opt$ByteToDouble((byte)0)); + assertDoubleEquals(-1D, $opt$ByteToDouble((byte)-1)); + assertDoubleEquals(51D, $opt$ByteToDouble((byte)51)); + assertDoubleEquals(-51D, $opt$ByteToDouble((byte)-51)); + assertDoubleEquals(127D, $opt$ByteToDouble((byte)127)); // 2^7 - 1 + assertDoubleEquals(-127D, $opt$ByteToDouble((byte)-127)); // -(2^7 - 1) + assertDoubleEquals(-128D, $opt$ByteToDouble((byte)-128)); // -(2^7) + } + + private static void shortToDouble() { + assertDoubleEquals(1D, $opt$ShortToDouble((short)1)); + assertDoubleEquals(0D, $opt$ShortToDouble((short)0)); + assertDoubleEquals(-1D, $opt$ShortToDouble((short)-1)); + assertDoubleEquals(51D, $opt$ShortToDouble((short)51)); + assertDoubleEquals(-51D, $opt$ShortToDouble((short)-51)); + assertDoubleEquals(32767D, $opt$ShortToDouble((short)32767)); // 2^15 - 1 + assertDoubleEquals(-32767D, $opt$ShortToDouble((short)-32767)); // -(2^15 - 1) + assertDoubleEquals(-32768D, $opt$ShortToDouble((short)-32768)); // -(2^15) + } + + private static void intToDouble() { + assertDoubleEquals(1D, $opt$IntToDouble(1)); + assertDoubleEquals(0D, $opt$IntToDouble(0)); + assertDoubleEquals(-1D, $opt$IntToDouble(-1)); + assertDoubleEquals(51D, $opt$IntToDouble(51)); + assertDoubleEquals(-51D, $opt$IntToDouble(-51)); + assertDoubleEquals(16777216D, $opt$IntToDouble(16777216)); // 2^24 + assertDoubleEquals(-16777216D, $opt$IntToDouble(-16777216)); // -(2^24) + assertDoubleEquals(2147483647D, $opt$IntToDouble(2147483647)); // 2^31 - 1 + assertDoubleEquals(-2147483648D, $opt$IntToDouble(-2147483648)); // -(2^31) + } + + private static void charToDouble() { + assertDoubleEquals(1D, $opt$CharToDouble((char)1)); + assertDoubleEquals(0D, $opt$CharToDouble((char)0)); + assertDoubleEquals(51D, $opt$CharToDouble((char)51)); + assertDoubleEquals(32767D, $opt$CharToDouble((char)32767)); // 2^15 - 1 + assertDoubleEquals(65535D, $opt$CharToDouble((char)65535)); // 2^16 - 1 + assertDoubleEquals(65535D, $opt$CharToDouble((char)-1)); + assertDoubleEquals(65485D, $opt$CharToDouble((char)-51)); + assertDoubleEquals(32769D, $opt$CharToDouble((char)-32767)); // -(2^15 - 1) + assertDoubleEquals(32768D, $opt$CharToDouble((char)-32768)); // -(2^15) + } + private static void longToInt() { assertIntEquals(1, $opt$LongToInt(1L)); assertIntEquals(0, $opt$LongToInt(0L)); @@ -175,10 +305,6 @@ public class Main { assertByteEquals((byte)-128, $opt$CharToByte((char)128)); // 2^7 assertByteEquals((byte)-1, $opt$CharToByte((char)32767)); // 2^15 - 1 assertByteEquals((byte)-1, $opt$CharToByte((char)65535)); // 2^16 - 1 - - assertByteEquals((byte)0, $opt$CharToByte('\u0000')); - assertByteEquals((byte)-1, $opt$CharToByte('\uFFFF')); // 2^16 - 1 - assertByteEquals((byte)-1, $opt$CharToByte((char)-1)); assertByteEquals((byte)-51, $opt$CharToByte((char)-51)); assertByteEquals((byte)-127, $opt$CharToByte((char)-127)); // -(2^7 - 1) @@ -186,6 +312,47 @@ public class Main { assertByteEquals((byte)127, $opt$CharToByte((char)-129)); // -(2^7 + 1) } + private static void byteToShort() { + assertShortEquals((short)1, $opt$ByteToShort((byte)1)); + assertShortEquals((short)0, $opt$ByteToShort((byte)0)); + assertShortEquals((short)-1, $opt$ByteToShort((byte)-1)); + assertShortEquals((short)51, $opt$ByteToShort((byte)51)); + assertShortEquals((short)-51, $opt$ByteToShort((byte)-51)); + assertShortEquals((short)127, $opt$ByteToShort((byte)127)); // 2^7 - 1 + assertShortEquals((short)-127, $opt$ByteToShort((byte)-127)); // -(2^7 - 1) + assertShortEquals((short)-128, $opt$ByteToShort((byte)-128)); // -(2^7) + } + + private static void intToShort() { + assertShortEquals((short)1, $opt$IntToShort(1)); + assertShortEquals((short)0, $opt$IntToShort(0)); + assertShortEquals((short)-1, $opt$IntToShort(-1)); + assertShortEquals((short)51, $opt$IntToShort(51)); + assertShortEquals((short)-51, $opt$IntToShort(-51)); + assertShortEquals((short)32767, $opt$IntToShort(32767)); // 2^15 - 1 + assertShortEquals((short)-32767, $opt$IntToShort(-32767)); // -(2^15 - 1) + assertShortEquals((short)-32768, $opt$IntToShort(-32768)); // -(2^15) + assertShortEquals((short)-32768, $opt$IntToShort(32768)); // 2^15 + assertShortEquals((short)32767, $opt$IntToShort(-32769)); // -(2^15 + 1) + assertShortEquals((short)-1, $opt$IntToShort(2147483647)); // 2^31 - 1 + assertShortEquals((short)0, $opt$IntToShort(-2147483648)); // -(2^31) + } + + private static void charToShort() { + assertShortEquals((short)1, $opt$CharToShort((char)1)); + assertShortEquals((short)0, $opt$CharToShort((char)0)); + assertShortEquals((short)51, $opt$CharToShort((char)51)); + assertShortEquals((short)32767, $opt$CharToShort((char)32767)); // 2^15 - 1 + assertShortEquals((short)-32768, $opt$CharToShort((char)32768)); // 2^15 + assertShortEquals((short)-32767, $opt$CharToShort((char)32769)); // 2^15 + assertShortEquals((short)-1, $opt$CharToShort((char)65535)); // 2^16 - 1 + assertShortEquals((short)-1, $opt$CharToShort((char)-1)); + assertShortEquals((short)-51, $opt$CharToShort((char)-51)); + assertShortEquals((short)-32767, $opt$CharToShort((char)-32767)); // -(2^15 - 1) + assertShortEquals((short)-32768, $opt$CharToShort((char)-32768)); // -(2^15) + assertShortEquals((short)32767, $opt$CharToShort((char)-32769)); // -(2^15 + 1) + } + private static void byteToChar() { assertCharEquals((char)1, $opt$ByteToChar((byte)1)); assertCharEquals((char)0, $opt$ByteToChar((byte)0)); @@ -233,6 +400,18 @@ public class Main { static long $opt$IntToLong(int a) { return a; } static long $opt$CharToLong(int a) { return a; } + // These methods produce int-to-float Dex instructions. + static float $opt$ByteToFloat(byte a) { return a; } + static float $opt$ShortToFloat(short a) { return a; } + static float $opt$IntToFloat(int a) { return a; } + static float $opt$CharToFloat(char a) { return a; } + + // These methods produce int-to-double Dex instructions. + static double $opt$ByteToDouble(byte a) { return a; } + static double $opt$ShortToDouble(short a) { return a; } + static double $opt$IntToDouble(int a) { return a; } + static double $opt$CharToDouble(int a) { return a; } + // These methods produce long-to-int Dex instructions. static int $opt$LongToInt(long a){ return (int)a; } static int $opt$LongLiteralToInt(){ return (int)42L; } @@ -242,6 +421,11 @@ public class Main { static byte $opt$IntToByte(int a){ return (byte)a; } static byte $opt$CharToByte(char a){ return (byte)a; } + // These methods produce int-to-short Dex instructions. + static short $opt$ByteToShort(byte a){ return (short)a; } + static short $opt$IntToShort(int a){ return (short)a; } + static short $opt$CharToShort(char a){ return (short)a; } + // These methods produce int-to-char Dex instructions. static char $opt$ByteToChar(byte a){ return (char)a; } static char $opt$ShortToChar(short a){ return (char)a; } diff --git a/test/425-invoke-super/expected.txt b/test/425-invoke-super/expected.txt index e69de29bb2..f7f6ae4911 100644 --- a/test/425-invoke-super/expected.txt +++ b/test/425-invoke-super/expected.txt @@ -0,0 +1 @@ +Test started diff --git a/test/425-invoke-super/src/Main.java b/test/425-invoke-super/src/Main.java index 1fb62d0871..f3166fd35b 100644 --- a/test/425-invoke-super/src/Main.java +++ b/test/425-invoke-super/src/Main.java @@ -39,6 +39,8 @@ public class Main { } public static void main(String[] args) throws Exception { + // Workaround for b/18051191. + System.out.println("Test started"); assertEquals(1, new B().$opt$bar()); assertEquals(1, new C().$opt$bar()); assertEquals(1, new D().$opt$bar()); diff --git a/test/428-optimizing-arith-rem/expected.txt b/test/428-optimizing-arith-rem/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/428-optimizing-arith-rem/expected.txt diff --git a/test/428-optimizing-arith-rem/info.txt b/test/428-optimizing-arith-rem/info.txt new file mode 100644 index 0000000000..3e37ffeee8 --- /dev/null +++ b/test/428-optimizing-arith-rem/info.txt @@ -0,0 +1 @@ +Tests for modulo (rem) operation. diff --git a/test/428-optimizing-arith-rem/src/Main.java b/test/428-optimizing-arith-rem/src/Main.java new file mode 100644 index 0000000000..46bd3c6ac5 --- /dev/null +++ b/test/428-optimizing-arith-rem/src/Main.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + public static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void expectDivisionByZero(int value) { + try { + $opt$Rem(value, 0); + throw new Error("Expected RuntimeException when modulo by 0"); + } catch (java.lang.RuntimeException e) { + } + try { + $opt$RemZero(value); + throw new Error("Expected RuntimeException when modulo by 0"); + } catch (java.lang.RuntimeException e) { + } + } + + public static void expectDivisionByZero(long value) { + try { + $opt$Rem(value, 0L); + throw new Error("Expected RuntimeException when modulo by 0"); + } catch (java.lang.RuntimeException e) { + } + try { + $opt$RemZero(value); + throw new Error("Expected RuntimeException when modulo by 0"); + } catch (java.lang.RuntimeException e) { + } + } + + public static void main(String[] args) { + rem(); + } + + public static void rem() { + remInt(); + remLong(); + } + + private static void remInt() { + expectEquals(2, $opt$RemConst(6)); + expectEquals(2, $opt$Rem(6, 4)); + expectEquals(2, $opt$Rem(6, -4)); + expectEquals(0, $opt$Rem(6, 3)); + expectEquals(0, $opt$Rem(6, -3)); + expectEquals(0, $opt$Rem(6, 1)); + expectEquals(0, $opt$Rem(6, -1)); + expectEquals(-1, $opt$Rem(-7, 3)); + expectEquals(-1, $opt$Rem(-7, -3)); + expectEquals(0, $opt$Rem(6, 6)); + expectEquals(0, $opt$Rem(-6, -6)); + expectEquals(7, $opt$Rem(7, 9)); + expectEquals(7, $opt$Rem(7, -9)); + expectEquals(-7, $opt$Rem(-7, 9)); + expectEquals(-7, $opt$Rem(-7, -9)); + + expectEquals(0, $opt$Rem(Integer.MAX_VALUE, 1)); + expectEquals(0, $opt$Rem(Integer.MAX_VALUE, -1)); + expectEquals(0, $opt$Rem(Integer.MIN_VALUE, 1)); + expectEquals(0, $opt$Rem(Integer.MIN_VALUE, -1)); // no overflow + expectEquals(-1, $opt$Rem(Integer.MIN_VALUE, Integer.MAX_VALUE)); + expectEquals(Integer.MAX_VALUE, $opt$Rem(Integer.MAX_VALUE, Integer.MIN_VALUE)); + + expectEquals(0, $opt$Rem(0, 7)); + expectEquals(0, $opt$Rem(0, Integer.MAX_VALUE)); + expectEquals(0, $opt$Rem(0, Integer.MIN_VALUE)); + + expectDivisionByZero(0); + expectDivisionByZero(1); + expectDivisionByZero(5); + expectDivisionByZero(Integer.MAX_VALUE); + expectDivisionByZero(Integer.MIN_VALUE); + } + + private static void remLong() { + expectEquals(2L, $opt$RemConst(6L)); + expectEquals(2L, $opt$Rem(6L, 4L)); + expectEquals(2L, $opt$Rem(6L, -4L)); + expectEquals(0L, $opt$Rem(6L, 3L)); + expectEquals(0L, $opt$Rem(6L, -3L)); + expectEquals(0L, $opt$Rem(6L, 1L)); + expectEquals(0L, $opt$Rem(6L, -1L)); + expectEquals(-1L, $opt$Rem(-7L, 3L)); + expectEquals(-1L, $opt$Rem(-7L, -3L)); + expectEquals(0L, $opt$Rem(6L, 6L)); + expectEquals(0L, $opt$Rem(-6L, -6L)); + expectEquals(7L, $opt$Rem(7L, 9L)); + expectEquals(7L, $opt$Rem(7L, -9L)); + expectEquals(-7L, $opt$Rem(-7L, 9L)); + expectEquals(-7L, $opt$Rem(-7L, -9L)); + + expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, 1L)); + expectEquals(0L, $opt$Rem(Integer.MAX_VALUE, -1L)); + expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, 1L)); + expectEquals(0L, $opt$Rem(Integer.MIN_VALUE, -1L)); // no overflow + expectEquals(-1L, $opt$Rem(Integer.MIN_VALUE, Integer.MAX_VALUE)); + expectEquals(Integer.MAX_VALUE, $opt$Rem(Integer.MAX_VALUE, Integer.MIN_VALUE)); + + expectEquals(0L, $opt$Rem(0L, 7L)); + expectEquals(0L, $opt$Rem(0L, Integer.MAX_VALUE)); + expectEquals(0L, $opt$Rem(0L, Integer.MIN_VALUE)); + + expectDivisionByZero(0L); + expectDivisionByZero(1L); + expectDivisionByZero(5L); + expectDivisionByZero(Integer.MAX_VALUE); + expectDivisionByZero(Integer.MIN_VALUE); + } + + static int $opt$Rem(int a, int b) { + return a % b; + } + + static int $opt$RemZero(int a) { + return a % 0; + } + + // Modulo by literals != 0 should not generate checks. + static int $opt$RemConst(int a) { + return a % 4; + } + + static long $opt$RemConst(long a) { + return a % 4L; + } + + static long $opt$Rem(long a, long b) { + return a % b; + } + + static long $opt$RemZero(long a) { + return a % 0L; + } +} diff --git a/test/429-ssa-builder/expected.txt b/test/429-ssa-builder/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/429-ssa-builder/expected.txt diff --git a/test/429-ssa-builder/info.txt b/test/429-ssa-builder/info.txt new file mode 100644 index 0000000000..509d00f5de --- /dev/null +++ b/test/429-ssa-builder/info.txt @@ -0,0 +1,3 @@ +Regression test for the type propagation phase of the optimizing +compiler, that used to crash when dealing with phi floating-point +equivalents. diff --git a/test/429-ssa-builder/src/Main.java b/test/429-ssa-builder/src/Main.java new file mode 100644 index 0000000000..32fcef0aa9 --- /dev/null +++ b/test/429-ssa-builder/src/Main.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + if (new Main().$opt$TestFloatPhi() != 33.0f) { + throw new Error("Unexpected result"); + } + } + + public float $opt$TestFloatPhi() { + float a = floatField; + float b = 42.0f; + if (test1) { + // The phi for `a` will be found to be of type float. + a = otherFloatField; + // The phi for `b` will be found to be of type int (constants in DEX). + b = 33.0f; + } + // Use a different condition to avoid having dx being too clever. + if (test2) { + // Type propagation now realizes that `b` must be of type float. So + // it requests a float equivalent for `b`. Because the phi for `a` is + // next to the phi for `b` in the phi list, the compiler used to crash, + // assuming that a float phi following a phi *must* be for the same DEX + // register. + a = b; + } + return a; + } + + float floatField = 4.2f; + float otherFloatField = 42.2f; + boolean test1 = true; + boolean test2 = true; +} diff --git a/test/430-live-register-slow-path/expected.txt b/test/430-live-register-slow-path/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/430-live-register-slow-path/expected.txt diff --git a/test/430-live-register-slow-path/info.txt b/test/430-live-register-slow-path/info.txt new file mode 100644 index 0000000000..6f2af283b9 --- /dev/null +++ b/test/430-live-register-slow-path/info.txt @@ -0,0 +1,2 @@ +Regression test for the linear scan register allocator. It used +to miscompute the number of live registers at a safepoint. diff --git a/test/430-live-register-slow-path/src/Main.java b/test/430-live-register-slow-path/src/Main.java new file mode 100644 index 0000000000..b84e6479f0 --- /dev/null +++ b/test/430-live-register-slow-path/src/Main.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + $opt$TestSlowPath(); + } + + public static void $opt$TestSlowPath() { + Object[] o = bar(); + assertEquals(0, o.length); + // The slowpath of the instanceof requires the live register + // holding `o` to be saved before going into runtime. The linear + // scan register allocator used to miscompute the number of + // live registers at a safepoint, so the place at which the register + // was saved was wrong. + doCall(o instanceof Interface[], o); + } + + public static void assertEquals(int a, int b) {} + public static boolean doCall(boolean val, Object o) { return val; } + + static Object[] bar() { return new Object[0]; } + + static interface Interface {} +} diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index f766b0a6cf..7674a8ab68 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -2,4 +2,8 @@ b/17790197 b/17978759 FloatBadArgReg negLong +sameFieldNames +b/18380491 +invoke-super abstract +BadCaseInOpRegRegReg Done! diff --git a/test/800-smali/smali/BadCaseInOpRegRegReg.smali b/test/800-smali/smali/BadCaseInOpRegRegReg.smali new file mode 100644 index 0000000000..2683790365 --- /dev/null +++ b/test/800-smali/smali/BadCaseInOpRegRegReg.smali @@ -0,0 +1,13 @@ +.class public LBadCaseInOpRegRegReg; + +.super Ljava/lang/Object; + +.method public static getInt()I + .registers 2 + const/4 v0, 0x0 + const/4 v1, 0x1 + add-int/2addr v0, v1 + add-int/lit8 v1, v0, 0x1 + mul-int v0, v1, v0 + return v0 +.end method diff --git a/test/800-smali/smali/b_18380491AbstractBase.smali b/test/800-smali/smali/b_18380491AbstractBase.smali new file mode 100644 index 0000000000..7aa1b1a12e --- /dev/null +++ b/test/800-smali/smali/b_18380491AbstractBase.smali @@ -0,0 +1,12 @@ +.class public LB18380491ActractBase; + +.super Ljava/lang/Object; + +.method public constructor <init>()V + .locals 0 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public abstract foo(I)I +.end method diff --git a/test/800-smali/smali/b_18380491ConcreteClass.smali b/test/800-smali/smali/b_18380491ConcreteClass.smali new file mode 100644 index 0000000000..db5ef3ba6b --- /dev/null +++ b/test/800-smali/smali/b_18380491ConcreteClass.smali @@ -0,0 +1,19 @@ +.class public LB18380491ConcreteClass; + +.super LB18380491ActractBase; + +.method public constructor <init>()V + .locals 0 + invoke-direct {p0}, LB18380491ActractBase;-><init>()V + return-void +.end method + +.method public foo(I)I + .locals 1 + if-eqz p1, :invoke_super_abstract + return p1 + :invoke_super_abstract + invoke-super {p0, p1}, LB18380491ActractBase;->foo(I)I + move-result v0 + return v0 +.end method diff --git a/test/800-smali/smali/sameFieldNames.smali b/test/800-smali/smali/sameFieldNames.smali new file mode 100644 index 0000000000..107161b538 --- /dev/null +++ b/test/800-smali/smali/sameFieldNames.smali @@ -0,0 +1,64 @@ +.class public LsameFieldNames; +.super Ljava/lang/Object; + +# Test multiple fields with the same name and different types. +# (Invalid in Java language but valid in bytecode.) +.field static public a:D +.field static public a:S +.field static public a:J +.field static public a:F +.field static public a:Z +.field static public a:I +.field static public a:B +.field static public a:C +.field static public a:Ljava/lang/Integer; +.field static public a:Ljava/lang/Long; +.field static public a:Ljava/lang/Float; +.field static public a:Ljava/lang/Double; +.field static public a:Ljava/lang/Boolean; +.field static public a:Ljava/lang/Void; +.field static public a:Ljava/lang/Short; +.field static public a:Ljava/lang/Char; +.field static public a:Ljava/lang/Byte; + +# Add some more fields to stress test the sorting for offset assignment. +.field static public b:C +.field static public c:J +.field static public d:C +.field static public e:B +.field static public f:C +.field static public g:J +.field static public h:C +.field static public i:J +.field static public j:I +.field static public k:J +.field static public l:J +.field static public m:I +.field static public n:J +.field static public o:I +.field static public p:Ljava/lang/Integer; +.field static public q:I +.field static public r:J +.field static public s:I +.field static public t:Ljava/lang/Integer; +.field static public u:I +.field static public v:J +.field static public w:I +.field static public x:Ljava/lang/Integer; +.field static public y:I +.field static public z:Ljava/lang/Integer; + +.method public static getInt()I + .locals 2 + const/4 v0, 2 + sput v0, LsameFieldNames;->a:I + sget-object v1, LsameFieldNames;->a:Ljava/lang/Integer; + const/4 v1, 0 + if-nez v1, :fail + const/4 v0, 7 + :ret + return v0 + :fail + const/4 v0, 0 + goto :ret +.end method diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index 014edc0fa4..8d318c354b 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.LinkedList; @@ -54,6 +55,12 @@ public class Main { testCases.add(new TestCase("FloatBadArgReg", "FloatBadArgReg", "getInt", new Object[]{100}, null, 100)); testCases.add(new TestCase("negLong", "negLong", "negLong", null, null, 122142L)); + testCases.add(new TestCase("sameFieldNames", "sameFieldNames", "getInt", null, null, 7)); + testCases.add(new TestCase("b/18380491", "B18380491ConcreteClass", "foo", + new Object[]{42}, null, 42)); + testCases.add(new TestCase("invoke-super abstract", "B18380491ConcreteClass", "foo", + new Object[]{0}, new AbstractMethodError(), null)); + testCases.add(new TestCase("BadCaseInOpRegRegReg", "BadCaseInOpRegRegReg", "getInt", null, null, 2)); } public void runTests() { @@ -115,6 +122,9 @@ public class Main { } catch (Throwable exc) { if (tc.expectedException == null) { errorReturn = new IllegalStateException("Did not expect exception", exc); + } else if (exc instanceof InvocationTargetException && exc.getCause() != null && + exc.getCause().getClass().equals(tc.expectedException.getClass())) { + // Expected exception is wrapped in InvocationTargetException. } else if (!tc.expectedException.getClass().equals(exc.getClass())) { errorReturn = new IllegalStateException("Expected " + tc.expectedException.getClass().getName() + diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 3b949d6ad0..29da2f6200 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -298,163 +298,39 @@ TEST_ART_BROKEN_DEFAULT_RUN_TESTS := # Known broken tests for the arm64 optimizing compiler backend. TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := \ - 001-HelloWorld \ - 002-sleep \ 003-omnibus-opcodes \ - 004-InterfaceTest \ - 004-JniTest \ 004-NativeAllocations \ 004-ReferenceMap \ - 004-SignalTest \ - 004-StackWalk \ - 004-UnsafeTest \ 005-annotations \ - 006-args \ - 007-count10 \ - 008-exceptions \ 009-instanceof \ 010-instance \ - 011-array-copy \ - 013-math2 \ - 014-math3 \ - 016-intern \ - 017-float \ - 018-stack-overflow \ - 019-wrong-array-type \ - 020-string \ - 021-string2 \ - 022-interface \ + 012-math \ 023-many-interfaces \ - 024-illegal-access \ - 025-access-controller \ - 026-access \ - 028-array-write \ - 029-assert \ - 030-bad-finalizer \ - 031-class-attributes \ - 032-concrete-sub \ - 033-class-init-deadlock \ - 034-call-null \ - 035-enum \ - 036-finalizer \ 037-inherit \ - 038-inner-null \ - 039-join-main \ - 040-miranda \ - 042-new-instance \ - 043-privates \ 044-proxy \ 045-reflect-array \ 046-reflect \ 047-returns \ - 049-show-object \ - 050-sync-test \ - 051-thread \ - 052-verifier-fun \ - 054-uncaught \ - 055-enum-performance \ - 056-const-string-jumbo \ - 058-enum-order \ - 061-out-of-memory \ 062-character-encodings \ 063-process-manager \ - 064-field-access \ - 065-mismatched-implements \ - 066-mismatched-super \ - 067-preemptive-unpark \ 068-classloader \ 069-field-type \ - 070-nio-buffer \ 071-dexfile \ - 072-precise-gc \ - 074-gc-thrash \ - 075-verification-error \ - 076-boolean-put \ - 077-method-override \ - 078-polymorphic-virtual \ - 079-phantom \ - 080-oom-throw \ - 081-hot-exceptions \ - 082-inline-execute \ 083-compiler-regressions \ - 084-class-init \ - 085-old-style-inner-class \ - 086-null-super \ - 087-gc-after-link \ - 088-monitor-verification \ - 090-loop-formation \ - 092-locale \ - 093-serialization \ - 094-pattern \ - 096-array-copy-concurrent-gc \ - 097-duplicate-method \ - 098-ddmc \ - 100-reflect2 \ - 101-fibonacci \ - 102-concurrent-gc \ - 103-string-append \ - 104-growth-limit \ - 105-invoke \ 106-exceptions2 \ 107-int-math2 \ - 108-check-cast \ - 109-suspend-check \ - 110-field-access \ - 111-unresolvable-exception \ - 112-double-math \ - 113-multidex \ 114-ParallelGC \ - 117-nopatchoat \ - 118-noimage-dex2oat \ - 119-noimage-patchoat \ - 120-hashcode \ - 121-modifiers \ - 121-simple-suspend-check \ - 122-npe \ - 123-compiler-regressions-mt \ - 124-missing-classes \ - 125-gc-and-classloading \ - 126-miranda-multidex \ 201-built-in-exception-detail-messages \ - 202-thread-oome \ - 300-package-override \ - 301-abstract-protected \ - 303-verification-stress \ - 304-method-tracing \ - 401-optimizing-compiler \ - 402-optimizing-control-flow \ - 403-optimizing-long \ - 404-optimizing-allocator \ - 405-optimizing-long-allocator \ - 406-fields \ 407-arrays \ - 409-materialized-condition \ - 410-floats \ - 411-optimizing-arith \ 412-new-array \ - 413-regalloc-regression \ - 414-optimizing-arith-sub \ - 414-static-fields \ - 415-optimizing-arith-neg \ - 416-optimizing-arith-not \ - 417-optimizing-arith-div \ - 418-const-string \ - 419-long-parameter \ - 420-const-class \ - 421-exceptions \ - 421-large-frame \ 422-instanceof \ 422-type-conversion \ - 423-invoke-interface \ 424-checkcast \ - 426-monitor \ - 427-bitwise \ 427-bounds \ - 700-LoadArgRegs \ + 428-optimizing-arith-rem \ + 430-live-register-slow-path \ 701-easy-div-rem \ - 702-LargeBranchOffset \ - 703-floating-point-div \ - 800-smali + 800-smali \ ifneq (,$(filter optimizing,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ diff --git a/test/run-test b/test/run-test index b43668d0e8..843714b949 100755 --- a/test/run-test +++ b/test/run-test @@ -501,6 +501,8 @@ if ! ulimit -S "$file_size_limit"; then fi good="no" +good_build="yes" +good_run="yes" if [ "$dev_mode" = "yes" ]; then "./${build}" 2>&1 build_exit="$?" @@ -548,7 +550,15 @@ else if [ "$build_exit" = '0' ]; then echo "${test_dir}: running..." 1>&2 "./${run}" $run_args "$@" >"$output" 2>&1 + run_exit="$?" + if [ "$run_exit" != "0" ]; then + echo "run exit status: $run_exit" 1>&2 + good_run="no" + else + good_run="yes" + fi else + good_build="no" cp "$build_output" "$output" echo "Failed to build in tmpdir=${tmp_dir} from oldwd=${oldwd} and cwd=`pwd`" >> "$output" echo "Non-canonical tmpdir was ${noncanonical_tmp_dir}" >> "$output" @@ -561,9 +571,11 @@ else fi ./$check_cmd "$expected" "$output" if [ "$?" = "0" ]; then - # output == expected - good="yes" - echo "${test_dir}: succeeded!" 1>&2 + if [ "$good_build" = "no" -o "$good_run" = "yes" ]; then + # output == expected + good="yes" + echo "${test_dir}: succeeded!" 1>&2 + fi fi fi |