diff options
Diffstat (limited to 'compiler')
51 files changed, 1676 insertions, 553 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index eb9ad475b4..84176a177b 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -180,7 +180,8 @@ LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ driver/compiler_options.h \ image_writer.h \ optimizing/locations.h \ - utils/arm/constants_arm.h + utils/arm/constants_arm.h \ + utils/dex_instruction_utils.h # $(1): target or host # $(2): ndebug or debug diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc index d311bc76ff..3daeb10223 100644 --- a/compiler/dex/global_value_numbering.cc +++ b/compiler/dex/global_value_numbering.cc @@ -15,7 +15,6 @@ */ #include "global_value_numbering.h" - #include "local_value_numbering.h" namespace art { @@ -31,8 +30,6 @@ GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAlloc modifications_allowed_(true), mode_(mode), global_value_map_(std::less<uint64_t>(), allocator->Adapter()), - field_index_map_(FieldReferenceComparator(), allocator->Adapter()), - field_index_reverse_map_(allocator->Adapter()), array_location_map_(ArrayLocationComparator(), allocator->Adapter()), array_location_reverse_map_(allocator->Adapter()), ref_set_map_(std::less<ValueNameSet>(), allocator->Adapter()), @@ -111,11 +108,7 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb, bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN || bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN_OBJECT || bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN_WIDE) && - (bb->first_mir_insn == bb->last_mir_insn || - (static_cast<int>(bb->first_mir_insn->dalvikInsn.opcode) == kMirOpPhi && - (bb->first_mir_insn->next == bb->last_mir_insn || - (static_cast<int>(bb->first_mir_insn->next->dalvikInsn.opcode) == kMirOpPhi && - bb->first_mir_insn->next->next == bb->last_mir_insn))))) { + bb->GetFirstNonPhiInsn() == bb->last_mir_insn) { merge_type = LocalValueNumbering::kReturnMerge; } // At least one predecessor must have been processed before this bb. @@ -145,19 +138,6 @@ bool GlobalValueNumbering::FinishBasicBlock(BasicBlock* bb) { return change; } -uint16_t GlobalValueNumbering::GetFieldId(const MirFieldInfo& field_info, uint16_t type) { - FieldReference key = { field_info.DeclaringDexFile(), field_info.DeclaringFieldIndex(), type }; - auto lb = field_index_map_.lower_bound(key); - if (lb != field_index_map_.end() && !field_index_map_.key_comp()(key, lb->first)) { - return lb->second; - } - DCHECK_LT(field_index_map_.size(), kNoValue); - uint16_t id = field_index_map_.size(); - auto it = field_index_map_.PutBefore(lb, key, id); - field_index_reverse_map_.push_back(&*it); - return id; -} - uint16_t GlobalValueNumbering::GetArrayLocation(uint16_t base, uint16_t index) { auto cmp = array_location_map_.key_comp(); ArrayLocation key = { base, index }; @@ -207,4 +187,20 @@ bool GlobalValueNumbering::NullCheckedInAllPredecessors( return true; } +bool GlobalValueNumbering::DivZeroCheckedInAllPredecessors( + const ScopedArenaVector<uint16_t>& merge_names) const { + // Implicit parameters: + // - *work_lvn: the LVN for which we're checking predecessors. + // - merge_lvns_: the predecessor LVNs. + DCHECK_EQ(merge_lvns_.size(), merge_names.size()); + for (size_t i = 0, size = merge_lvns_.size(); i != size; ++i) { + const LocalValueNumbering* pred_lvn = merge_lvns_[i]; + uint16_t value_name = merge_names[i]; + if (!pred_lvn->IsValueDivZeroChecked(value_name)) { + return false; + } + } + return true; +} + } // namespace art diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h index 72d111244d..d72144a051 100644 --- a/compiler/dex/global_value_numbering.h +++ b/compiler/dex/global_value_numbering.h @@ -39,6 +39,12 @@ class GlobalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { cu->mir_graph->GetMaxNestedLoops() > kMaxAllowedNestedLoops; } + // Instance and static field id map is held by MIRGraph to avoid multiple recalculations + // when doing LVN. + template <typename Container> // Container of MirIFieldLoweringInfo or MirSFieldLoweringInfo. + static uint16_t* PrepareGvnFieldIds(ScopedArenaAllocator* allocator, + const Container& field_infos); + GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator, Mode mode); ~GlobalValueNumbering(); @@ -114,34 +120,24 @@ class GlobalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { return (it != global_value_map_.end() && it->second == value); } - // FieldReference represents a unique resolved field. - struct FieldReference { - const DexFile* dex_file; - uint16_t field_idx; - uint16_t type; // See comments for LocalValueNumbering::kFieldTypeCount. - }; - - struct FieldReferenceComparator { - bool operator()(const FieldReference& lhs, const FieldReference& rhs) const { - if (lhs.field_idx != rhs.field_idx) { - return lhs.field_idx < rhs.field_idx; - } - // If the field_idx and dex_file match, the type must also match. - DCHECK(lhs.dex_file != rhs.dex_file || lhs.type == rhs.type); - return lhs.dex_file < rhs.dex_file; - } - }; + // Get an instance field id. + uint16_t GetIFieldId(MIR* mir) { + return GetMirGraph()->GetGvnIFieldId(mir); + } - // Maps field key to field id for resolved fields. - typedef ScopedArenaSafeMap<FieldReference, uint32_t, FieldReferenceComparator> FieldIndexMap; + // Get a static field id. + uint16_t GetSFieldId(MIR* mir) { + return GetMirGraph()->GetGvnSFieldId(mir); + } - // Get a field id. - uint16_t GetFieldId(const MirFieldInfo& field_info, uint16_t type); + // Get an instance field type based on field id. + uint16_t GetIFieldType(uint16_t field_id) { + return static_cast<uint16_t>(GetMirGraph()->GetIFieldLoweringInfo(field_id).MemAccessType()); + } - // Get a field type based on field id. - uint16_t GetFieldType(uint16_t field_id) { - DCHECK_LT(field_id, field_index_reverse_map_.size()); - return field_index_reverse_map_[field_id]->first.type; + // Get a static field type based on field id. + uint16_t GetSFieldType(uint16_t field_id) { + return static_cast<uint16_t>(GetMirGraph()->GetSFieldLoweringInfo(field_id).MemAccessType()); } struct ArrayLocation { @@ -199,6 +195,8 @@ class GlobalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { bool NullCheckedInAllPredecessors(const ScopedArenaVector<uint16_t>& merge_names) const; + bool DivZeroCheckedInAllPredecessors(const ScopedArenaVector<uint16_t>& merge_names) const; + CompilationUnit* GetCompilationUnit() const { return cu_; } @@ -239,8 +237,6 @@ class GlobalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { Mode mode_; ValueMap global_value_map_; - FieldIndexMap field_index_map_; - ScopedArenaVector<const FieldIndexMap::value_type*> field_index_reverse_map_; ArrayLocationMap array_location_map_; ScopedArenaVector<const ArrayLocationMap::value_type*> array_location_reverse_map_; RefSetIdMap ref_set_map_; @@ -268,6 +264,32 @@ inline uint16_t GlobalValueNumbering::NewValueName() { return last_value_; } +template <typename Container> // Container of MirIFieldLoweringInfo or MirSFieldLoweringInfo. +uint16_t* GlobalValueNumbering::PrepareGvnFieldIds(ScopedArenaAllocator* allocator, + const Container& field_infos) { + size_t size = field_infos.size(); + uint16_t* field_ids = reinterpret_cast<uint16_t*>(allocator->Alloc(size * sizeof(uint16_t), + kArenaAllocMisc)); + for (size_t i = 0u; i != size; ++i) { + size_t idx = i; + const MirFieldInfo& cur_info = field_infos[i]; + if (cur_info.IsResolved()) { + for (size_t j = 0; j != i; ++j) { + const MirFieldInfo& prev_info = field_infos[j]; + if (prev_info.IsResolved() && + prev_info.DeclaringDexFile() == cur_info.DeclaringDexFile() && + prev_info.DeclaringFieldIndex() == cur_info.DeclaringFieldIndex()) { + DCHECK_EQ(cur_info.MemAccessType(), prev_info.MemAccessType()); + idx = j; + break; + } + } + } + field_ids[i] = idx; + } + return field_ids; +} + } // namespace art #endif // ART_COMPILER_DEX_GLOBAL_VALUE_NUMBERING_H_ diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index 35d5b995cf..7e3b4d8adf 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -17,6 +17,7 @@ #include "compiler_internals.h" #include "dataflow_iterator.h" #include "dataflow_iterator-inl.h" +#include "dex/mir_field_info.h" #include "global_value_numbering.h" #include "local_value_numbering.h" #include "gtest/gtest.h" @@ -32,6 +33,7 @@ class GlobalValueNumberingTest : public testing::Test { uintptr_t declaring_dex_file; uint16_t declaring_field_idx; bool is_volatile; + DexMemAccessType type; }; struct SFieldDef { @@ -39,6 +41,7 @@ class GlobalValueNumberingTest : public testing::Test { uintptr_t declaring_dex_file; uint16_t declaring_field_idx; bool is_volatile; + DexMemAccessType type; }; struct BBDef { @@ -131,18 +134,19 @@ class GlobalValueNumberingTest : public testing::Test { { bb, opcode, 0u, 0u, 2, { src, src + 1 }, 2, { reg, reg + 1 } } #define DEF_PHI2(bb, reg, src1, src2) \ { bb, static_cast<Instruction::Code>(kMirOpPhi), 0, 0u, 2u, { src1, src2 }, 1, { reg } } +#define DEF_DIV_REM(bb, opcode, result, dividend, divisor) \ + { bb, opcode, 0u, 0u, 2, { dividend, divisor }, 1, { result } } void DoPrepareIFields(const IFieldDef* defs, size_t count) { cu_.mir_graph->ifield_lowering_infos_.clear(); cu_.mir_graph->ifield_lowering_infos_.reserve(count); for (size_t i = 0u; i != count; ++i) { const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx); + MirIFieldLoweringInfo field_info(def->field_idx, def->type); if (def->declaring_dex_file != 0u) { field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = 0u | // Without kFlagIsStatic. - (def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u); + field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); } cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); } @@ -158,15 +162,14 @@ class GlobalValueNumberingTest : public testing::Test { cu_.mir_graph->sfield_lowering_infos_.reserve(count); for (size_t i = 0u; i != count; ++i) { const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx); + MirSFieldLoweringInfo field_info(def->field_idx, def->type); // Mark even unresolved fields as initialized. - field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic | - MirSFieldLoweringInfo::kFlagClassIsInitialized; + field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by GVN. if (def->declaring_dex_file != 0u) { field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ |= (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u); + field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); } cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); } @@ -238,12 +241,16 @@ class GlobalValueNumberingTest : public testing::Test { mir->dalvikInsn.opcode = def->opcode; mir->dalvikInsn.vB = static_cast<int32_t>(def->value); mir->dalvikInsn.vB_wide = def->value; - if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) { + if (IsInstructionIGetOrIPut(def->opcode)) { ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); mir->meta.ifield_lowering_info = def->field_info; - } else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) { + ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), + IGetOrIPutMemAccessType(def->opcode)); + } else if (IsInstructionSGetOrSPut(def->opcode)) { ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); mir->meta.sfield_lowering_info = def->field_info; + ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), + SGetOrSPutMemAccessType(def->opcode)); } else if (def->opcode == static_cast<Instruction::Code>(kMirOpPhi)) { mir->meta.phi_incoming = static_cast<BasicBlockId*>( allocator_->Alloc(def->num_uses * sizeof(BasicBlockId), kArenaAllocDFInfo)); @@ -288,6 +295,10 @@ class GlobalValueNumberingTest : public testing::Test { cu_.mir_graph->ComputeDominators(); cu_.mir_graph->ComputeTopologicalSortOrder(); cu_.mir_graph->SSATransformationEnd(); + cu_.mir_graph->temp_.gvn.ifield_ids_ = GlobalValueNumbering::PrepareGvnFieldIds( + allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); + cu_.mir_graph->temp_.gvn.sfield_ids_ = GlobalValueNumbering::PrepareGvnFieldIds( + allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); ASSERT_TRUE(gvn_ == nullptr); gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), GlobalValueNumbering::kModeGvn)); @@ -498,18 +509,18 @@ GlobalValueNumberingTestTwoNestedLoops::GlobalValueNumberingTestTwoNestedLoops() TEST_F(GlobalValueNumberingTestDiamond, NonAliasingIFields) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Int. - { 4u, 1u, 4u, false }, // Short. - { 5u, 1u, 5u, false }, // Char. - { 6u, 0u, 0u, false }, // Unresolved, Short. - { 7u, 1u, 7u, false }, // Int. - { 8u, 0u, 0u, false }, // Unresolved, Int. - { 9u, 1u, 9u, false }, // Int. - { 10u, 1u, 10u, false }, // Int. - { 11u, 1u, 11u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, + { 4u, 1u, 4u, false, kDexMemAccessShort }, + { 5u, 1u, 5u, false, kDexMemAccessChar }, + { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. + { 7u, 1u, 7u, false, kDexMemAccessWord }, + { 8u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved. + { 9u, 1u, 9u, false, kDexMemAccessWord }, + { 10u, 1u, 10u, false, kDexMemAccessWord }, + { 11u, 1u, 11u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -604,15 +615,15 @@ TEST_F(GlobalValueNumberingTestDiamond, NonAliasingIFields) { TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsSingleObject) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Int. - { 4u, 1u, 4u, false }, // Short. - { 5u, 1u, 5u, false }, // Char. - { 6u, 0u, 0u, false }, // Unresolved, Short. - { 7u, 1u, 7u, false }, // Int. - { 8u, 1u, 8u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, + { 4u, 1u, 4u, false, kDexMemAccessShort }, + { 5u, 1u, 5u, false, kDexMemAccessChar }, + { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. + { 7u, 1u, 7u, false, kDexMemAccessWord }, + { 8u, 1u, 8u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -671,15 +682,15 @@ TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsSingleObject) { TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsTwoObjects) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Int. - { 4u, 1u, 4u, false }, // Short. - { 5u, 1u, 5u, false }, // Char. - { 6u, 0u, 0u, false }, // Unresolved, Short. - { 7u, 1u, 7u, false }, // Int. - { 8u, 1u, 8u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, + { 4u, 1u, 4u, false, kDexMemAccessShort }, + { 5u, 1u, 5u, false, kDexMemAccessChar }, + { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. + { 7u, 1u, 7u, false, kDexMemAccessWord }, + { 8u, 1u, 8u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -740,15 +751,15 @@ TEST_F(GlobalValueNumberingTestDiamond, AliasingIFieldsTwoObjects) { TEST_F(GlobalValueNumberingTestDiamond, SFields) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Int. - { 4u, 1u, 4u, false }, // Short. - { 5u, 1u, 5u, false }, // Char. - { 6u, 0u, 0u, false }, // Unresolved, Short. - { 7u, 1u, 7u, false }, // Int. - { 8u, 1u, 8u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, + { 4u, 1u, 4u, false, kDexMemAccessShort }, + { 5u, 1u, 5u, false, kDexMemAccessChar }, + { 6u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. + { 7u, 1u, 7u, false, kDexMemAccessWord }, + { 8u, 1u, 8u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -1078,18 +1089,18 @@ TEST_F(GlobalValueNumberingTestDiamond, PhiWide) { TEST_F(GlobalValueNumberingTestLoop, NonAliasingIFields) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Int. - { 4u, 1u, 4u, false }, // Int. - { 5u, 1u, 5u, false }, // Short. - { 6u, 1u, 6u, false }, // Char. - { 7u, 0u, 0u, false }, // Unresolved, Short. - { 8u, 1u, 8u, false }, // Int. - { 9u, 0u, 0u, false }, // Unresolved, Int. - { 10u, 1u, 10u, false }, // Int. - { 11u, 1u, 11u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, + { 4u, 1u, 4u, false, kDexMemAccessWord }, + { 5u, 1u, 5u, false, kDexMemAccessShort }, + { 6u, 1u, 6u, false, kDexMemAccessChar }, + { 7u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. + { 8u, 1u, 8u, false, kDexMemAccessWord }, + { 9u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved. + { 10u, 1u, 10u, false, kDexMemAccessWord }, + { 11u, 1u, 11u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -1201,14 +1212,14 @@ TEST_F(GlobalValueNumberingTestLoop, NonAliasingIFields) { TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsSingleObject) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Int. - { 4u, 1u, 4u, false }, // Int. - { 5u, 1u, 5u, false }, // Short. - { 6u, 1u, 6u, false }, // Char. - { 7u, 0u, 0u, false }, // Unresolved, Short. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, + { 4u, 1u, 4u, false, kDexMemAccessWord }, + { 5u, 1u, 5u, false, kDexMemAccessShort }, + { 6u, 1u, 6u, false, kDexMemAccessChar }, + { 7u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -1272,14 +1283,14 @@ TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsSingleObject) { TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsTwoObjects) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. - { 3u, 1u, 3u, false }, // Short. - { 4u, 1u, 4u, false }, // Char. - { 5u, 0u, 0u, false }, // Unresolved, Short. - { 6u, 1u, 6u, false }, // Int. - { 7u, 1u, 7u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, + { 3u, 1u, 3u, false, kDexMemAccessShort }, + { 4u, 1u, 4u, false, kDexMemAccessChar }, + { 5u, 0u, 0u, false, kDexMemAccessShort }, // Unresolved. + { 6u, 1u, 6u, false, kDexMemAccessWord }, + { 7u, 1u, 7u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -1341,7 +1352,7 @@ TEST_F(GlobalValueNumberingTestLoop, AliasingIFieldsTwoObjects) { TEST_F(GlobalValueNumberingTestLoop, IFieldToBaseDependency) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // For the IGET that loads sreg 3u using base 2u, the following IPUT creates a dependency @@ -1366,9 +1377,9 @@ TEST_F(GlobalValueNumberingTestLoop, IFieldToBaseDependency) { TEST_F(GlobalValueNumberingTestLoop, SFields) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. - { 2u, 1u, 2u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -1562,8 +1573,8 @@ TEST_F(GlobalValueNumberingTestLoop, Phi) { TEST_F(GlobalValueNumberingTestCatch, IFields) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, - { 1u, 1u, 1u, false }, + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 200u), @@ -1608,8 +1619,8 @@ TEST_F(GlobalValueNumberingTestCatch, IFields) { TEST_F(GlobalValueNumberingTestCatch, SFields) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, - { 1u, 1u, 1u, false }, + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_SGET(3, Instruction::SGET, 0u, 0u), @@ -1731,8 +1742,8 @@ TEST_F(GlobalValueNumberingTestCatch, Phi) { TEST_F(GlobalValueNumberingTest, NullCheckIFields) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Object. - { 1u, 1u, 1u, false }, // Object. + { 0u, 1u, 0u, false, kDexMemAccessObject }, // Object. + { 1u, 1u, 1u, false, kDexMemAccessObject }, // Object. }; static const BBDef bbs[] = { DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), @@ -1780,8 +1791,8 @@ TEST_F(GlobalValueNumberingTest, NullCheckIFields) { TEST_F(GlobalValueNumberingTest, NullCheckSFields) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, // Object. - { 1u, 1u, 1u, false }, // Object. + { 0u, 1u, 0u, false, kDexMemAccessObject }, + { 1u, 1u, 1u, false, kDexMemAccessObject }, }; static const BBDef bbs[] = { DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), @@ -1907,12 +1918,12 @@ TEST_F(GlobalValueNumberingTestDiamond, RangeCheckArrays) { TEST_F(GlobalValueNumberingTestDiamond, MergeSameValueInDifferentMemoryLocations) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, // Int. - { 1u, 1u, 1u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessWord }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(3, Instruction::NEW_INSTANCE, 100u), @@ -1977,7 +1988,7 @@ TEST_F(GlobalValueNumberingTest, InfiniteLocationLoop) { // LVN's aliasing_array_value_map_'s load_value_map for BBs #9, #4, #5, #7 because of the // DFS ordering of LVN evaluation. static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Object. + { 0u, 1u, 0u, false, kDexMemAccessObject }, }; static const BBDef bbs[] = { DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()), @@ -2015,7 +2026,7 @@ TEST_F(GlobalValueNumberingTest, InfiniteLocationLoop) { TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, IFieldAndPhi) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessObject }, }; static const MIRDef mirs[] = { DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), @@ -2052,10 +2063,10 @@ TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, IFieldAndPhi) { TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, NullCheck) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessObject }, }; static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessObject }, }; static const MIRDef mirs[] = { DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), @@ -2143,7 +2154,7 @@ TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, NullCheck) { TEST_F(GlobalValueNumberingTestTwoNestedLoops, IFieldAndPhi) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, false }, // Int. + { 0u, 1u, 0u, false, kDexMemAccessObject }, }; static const MIRDef mirs[] = { DEF_MOVE(3, Instruction::MOVE_OBJECT, 0u, 100u), @@ -2213,4 +2224,45 @@ TEST_F(GlobalValueNumberingTest, NormalPathToCatchEntry) { PerformGVN(); } +TEST_F(GlobalValueNumberingTestDiamond, DivZeroCheckDiamond) { + static const MIRDef mirs[] = { + DEF_DIV_REM(3u, Instruction::DIV_INT, 1u, 20u, 21u), + DEF_DIV_REM(3u, Instruction::DIV_INT, 2u, 24u, 21u), + DEF_DIV_REM(3u, Instruction::DIV_INT, 3u, 20u, 23u), + DEF_DIV_REM(4u, Instruction::DIV_INT, 4u, 24u, 22u), + DEF_DIV_REM(4u, Instruction::DIV_INT, 9u, 24u, 25u), + DEF_DIV_REM(5u, Instruction::DIV_INT, 5u, 24u, 21u), + DEF_DIV_REM(5u, Instruction::DIV_INT, 10u, 24u, 26u), + DEF_PHI2(6u, 27u, 25u, 26u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 12u, 20u, 27u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 6u, 24u, 21u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 7u, 20u, 23u), + DEF_DIV_REM(6u, Instruction::DIV_INT, 8u, 20u, 22u), + }; + + static const bool expected_ignore_div_zero_check[] = { + false, // New divisor seen. + true, // Eliminated since it has first divisor as first one. + false, // New divisor seen. + false, // New divisor seen. + false, // New divisor seen. + true, // Eliminated in dominating block. + false, // New divisor seen. + false, // Phi node. + true, // Eliminated on both sides of diamond and merged via phi. + true, // Eliminated in dominating block. + true, // Eliminated in dominating block. + false, // Only eliminated on one path of diamond. + }; + + PrepareMIRs(mirs); + PerformGVN(); + PerformGVNCodeModifications(); + ASSERT_EQ(arraysize(expected_ignore_div_zero_check), mir_count_); + for (size_t i = 0u; i != mir_count_; ++i) { + int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; + EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; + } +} + } // namespace art diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index c1ce2ac016..c502b0cb37 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -56,7 +56,7 @@ class LocalValueNumbering::AliasingIFieldVersions { public: static uint16_t StartMemoryVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, uint16_t field_id) { - uint16_t type = gvn->GetFieldType(field_id); + uint16_t type = gvn->GetIFieldType(field_id); return gvn->LookupValue(kAliasingIFieldStartVersionOp, field_id, lvn->global_memory_version_, lvn->unresolved_ifield_version_[type]); } @@ -75,7 +75,7 @@ class LocalValueNumbering::AliasingIFieldVersions { static uint16_t LookupMergeValue(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, uint16_t field_id, uint16_t base) { // If the base/field_id is non-aliasing in lvn, use the non-aliasing value. - uint16_t type = gvn->GetFieldType(field_id); + uint16_t type = gvn->GetIFieldType(field_id); if (lvn->IsNonAliasingIField(base, field_id, type)) { uint16_t loc = gvn->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); auto lb = lvn->non_aliasing_ifield_value_map_.find(loc); @@ -89,7 +89,7 @@ class LocalValueNumbering::AliasingIFieldVersions { static bool HasNewBaseVersion(GlobalValueNumbering* gvn, const LocalValueNumbering* lvn, uint16_t field_id) { - uint16_t type = gvn->GetFieldType(field_id); + uint16_t type = gvn->GetIFieldType(field_id); return lvn->unresolved_ifield_version_[type] == lvn->merge_new_memory_version_ || lvn->global_memory_version_ == lvn->merge_new_memory_version_; } @@ -339,6 +339,7 @@ LocalValueNumbering::LocalValueNumbering(GlobalValueNumbering* gvn, uint16_t id, escaped_array_clobber_set_(EscapedArrayClobberKeyComparator(), allocator->Adapter()), range_checked_(RangeCheckKeyComparator() , allocator->Adapter()), null_checked_(std::less<uint16_t>(), allocator->Adapter()), + div_zero_checked_(std::less<uint16_t>(), allocator->Adapter()), merge_names_(allocator->Adapter()), merge_map_(std::less<ScopedArenaVector<BasicBlockId>>(), allocator->Adapter()), merge_new_memory_version_(kNoValue) { @@ -362,7 +363,8 @@ bool LocalValueNumbering::Equals(const LocalValueNumbering& other) const { escaped_ifield_clobber_set_ == other.escaped_ifield_clobber_set_ && escaped_array_clobber_set_ == other.escaped_array_clobber_set_ && range_checked_ == other.range_checked_ && - null_checked_ == other.null_checked_; + null_checked_ == other.null_checked_ && + div_zero_checked_ == other.div_zero_checked_; } void LocalValueNumbering::MergeOne(const LocalValueNumbering& other, MergeType merge_type) { @@ -379,6 +381,7 @@ void LocalValueNumbering::MergeOne(const LocalValueNumbering& other, MergeType m non_aliasing_refs_ = other.non_aliasing_refs_; range_checked_ = other.range_checked_; null_checked_ = other.null_checked_; + div_zero_checked_ = other.div_zero_checked_; const BasicBlock* pred_bb = gvn_->GetBasicBlock(other.Id()); if (GlobalValueNumbering::HasNullCheckLastInsn(pred_bb, Id())) { @@ -699,6 +702,28 @@ void LocalValueNumbering::MergeNullChecked() { } } +void LocalValueNumbering::MergeDivZeroChecked() { + DCHECK_GE(gvn_->merge_lvns_.size(), 2u); + + // Find the LVN with the least entries in the set. + const LocalValueNumbering* least_entries_lvn = gvn_->merge_lvns_[0]; + for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { + if (lvn->div_zero_checked_.size() < least_entries_lvn->div_zero_checked_.size()) { + least_entries_lvn = lvn; + } + } + + // For each div-zero value name check if it's div-zero checked in all the LVNs. + for (const auto& value_name : least_entries_lvn->div_zero_checked_) { + // Merge null_checked_ for this ref. + merge_names_.clear(); + merge_names_.resize(gvn_->merge_lvns_.size(), value_name); + if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { + div_zero_checked_.insert(div_zero_checked_.end(), value_name); + } + } +} + void LocalValueNumbering::MergeSFieldValues(const SFieldToValueMap::value_type& entry, SFieldToValueMap::iterator hint) { uint16_t field_id = entry.first; @@ -711,7 +736,7 @@ void LocalValueNumbering::MergeSFieldValues(const SFieldToValueMap::value_type& if (it != lvn->sfield_value_map_.end()) { value_name = it->second; } else { - uint16_t type = gvn_->GetFieldType(field_id); + uint16_t type = gvn_->GetSFieldType(field_id); value_name = gvn_->LookupValue(kResolvedSFieldOp, field_id, lvn->unresolved_sfield_version_[type], lvn->global_memory_version_); @@ -931,6 +956,9 @@ void LocalValueNumbering::Merge(MergeType merge_type) { // Merge null_checked_. We may later insert more, such as merged object field values. MergeNullChecked(); + // Now merge the div_zero_checked_. + MergeDivZeroChecked(); + if (merge_type == kCatchMerge) { // Memory is clobbered. New memory version already created, don't merge aliasing locations. return; @@ -1054,6 +1082,20 @@ void LocalValueNumbering::HandleRangeCheck(MIR* mir, uint16_t array, uint16_t in } } +void LocalValueNumbering::HandleDivZeroCheck(MIR* mir, uint16_t reg) { + auto lb = div_zero_checked_.lower_bound(reg); + if (lb != div_zero_checked_.end() && *lb == reg) { + if (LIKELY(gvn_->CanModify())) { + if (gvn_->GetCompilationUnit()->verbose) { + LOG(INFO) << "Removing div zero check for 0x" << std::hex << mir->offset; + } + mir->optimization_flags |= MIR_IGNORE_DIV_ZERO_CHECK; + } + } else { + div_zero_checked_.insert(lb, reg); + } +} + void LocalValueNumbering::HandlePutObject(MIR* mir) { // If we're storing a non-aliasing reference, stop tracking it as non-aliasing now. uint16_t base = GetOperandValue(mir->ssa_rep->uses[0]); @@ -1139,6 +1181,9 @@ uint16_t LocalValueNumbering::HandlePhi(MIR* mir) { if (!wide && gvn_->NullCheckedInAllPredecessors(merge_names_)) { null_checked_.insert(value_name); } + if (gvn_->DivZeroCheckedInAllPredecessors(merge_names_)) { + div_zero_checked_.insert(value_name); + } } } if (wide) { @@ -1150,12 +1195,11 @@ uint16_t LocalValueNumbering::HandlePhi(MIR* mir) { } uint16_t LocalValueNumbering::HandleAGet(MIR* mir, uint16_t opcode) { - // uint16_t type = opcode - Instruction::AGET; uint16_t array = GetOperandValue(mir->ssa_rep->uses[0]); HandleNullCheck(mir, array); uint16_t index = GetOperandValue(mir->ssa_rep->uses[1]); HandleRangeCheck(mir, array, index); - uint16_t type = opcode - Instruction::AGET; + uint16_t type = AGetMemAccessType(static_cast<Instruction::Code>(opcode)); // Establish value number for loaded register. uint16_t res; if (IsNonAliasingArray(array, type)) { @@ -1182,7 +1226,7 @@ void LocalValueNumbering::HandleAPut(MIR* mir, uint16_t opcode) { uint16_t index = GetOperandValue(mir->ssa_rep->uses[index_idx]); HandleRangeCheck(mir, array, index); - uint16_t type = opcode - Instruction::APUT; + uint16_t type = APutMemAccessType(static_cast<Instruction::Code>(opcode)); uint16_t value = (opcode == Instruction::APUT_WIDE) ? GetOperandValueWide(mir->ssa_rep->uses[0]) : GetOperandValue(mir->ssa_rep->uses[0]); @@ -1224,8 +1268,8 @@ uint16_t LocalValueNumbering::HandleIGet(MIR* mir, uint16_t opcode) { // Use result s_reg - will be unique. res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); } else { - uint16_t type = opcode - Instruction::IGET; - uint16_t field_id = gvn_->GetFieldId(field_info, type); + uint16_t type = IGetMemAccessType(static_cast<Instruction::Code>(opcode)); + uint16_t field_id = gvn_->GetIFieldId(mir); if (IsNonAliasingIField(base, field_id, type)) { uint16_t loc = gvn_->LookupValue(kNonAliasingIFieldLocOp, base, field_id, type); auto lb = non_aliasing_ifield_value_map_.lower_bound(loc); @@ -1249,10 +1293,10 @@ uint16_t LocalValueNumbering::HandleIGet(MIR* mir, uint16_t opcode) { } void LocalValueNumbering::HandleIPut(MIR* mir, uint16_t opcode) { - uint16_t type = opcode - Instruction::IPUT; int base_reg = (opcode == Instruction::IPUT_WIDE) ? 2 : 1; uint16_t base = GetOperandValue(mir->ssa_rep->uses[base_reg]); HandleNullCheck(mir, base); + uint16_t type = IPutMemAccessType(static_cast<Instruction::Code>(opcode)); const MirFieldInfo& field_info = gvn_->GetMirGraph()->GetIFieldLoweringInfo(mir); if (!field_info.IsResolved()) { // Unresolved fields always alias with everything of the same type. @@ -1272,7 +1316,7 @@ void LocalValueNumbering::HandleIPut(MIR* mir, uint16_t opcode) { // Aliasing fields of the same type may have been overwritten. auto it = aliasing_ifield_value_map_.begin(), end = aliasing_ifield_value_map_.end(); while (it != end) { - if (gvn_->GetFieldType(it->first) != type) { + if (gvn_->GetIFieldType(it->first) != type) { ++it; } else { it = aliasing_ifield_value_map_.erase(it); @@ -1282,7 +1326,7 @@ void LocalValueNumbering::HandleIPut(MIR* mir, uint16_t opcode) { // Nothing to do, resolved volatile fields always get a new memory version anyway and // can't alias with resolved non-volatile fields. } else { - uint16_t field_id = gvn_->GetFieldId(field_info, type); + uint16_t field_id = gvn_->GetIFieldId(mir); uint16_t value = (opcode == Instruction::IPUT_WIDE) ? GetOperandValueWide(mir->ssa_rep->uses[0]) : GetOperandValue(mir->ssa_rep->uses[0]); @@ -1333,8 +1377,8 @@ uint16_t LocalValueNumbering::HandleSGet(MIR* mir, uint16_t opcode) { // Use result s_reg - will be unique. res = gvn_->LookupValue(kNoValue, mir->ssa_rep->defs[0], kNoValue, kNoValue); } else { - uint16_t type = opcode - Instruction::SGET; - uint16_t field_id = gvn_->GetFieldId(field_info, type); + uint16_t type = SGetMemAccessType(static_cast<Instruction::Code>(opcode)); + uint16_t field_id = gvn_->GetSFieldId(mir); auto lb = sfield_value_map_.lower_bound(field_id); if (lb != sfield_value_map_.end() && lb->first == field_id) { res = lb->second; @@ -1362,7 +1406,7 @@ void LocalValueNumbering::HandleSPut(MIR* mir, uint16_t opcode) { // Class initialization can call arbitrary functions, we need to wipe aliasing values. HandleInvokeOrClInitOrAcquireOp(mir); } - uint16_t type = opcode - Instruction::SPUT; + uint16_t type = SPutMemAccessType(static_cast<Instruction::Code>(opcode)); if (!field_info.IsResolved()) { // Unresolved fields always alias with everything of the same type. // Use mir->offset as modifier; without elaborate inlining, it will be unique. @@ -1373,7 +1417,7 @@ void LocalValueNumbering::HandleSPut(MIR* mir, uint16_t opcode) { // Nothing to do, resolved volatile fields always get a new memory version anyway and // can't alias with resolved non-volatile fields. } else { - uint16_t field_id = gvn_->GetFieldId(field_info, type); + uint16_t field_id = gvn_->GetSFieldId(mir); uint16_t value = (opcode == Instruction::SPUT_WIDE) ? GetOperandValueWide(mir->ssa_rep->uses[0]) : GetOperandValue(mir->ssa_rep->uses[0]); @@ -1397,7 +1441,7 @@ void LocalValueNumbering::HandleSPut(MIR* mir, uint16_t opcode) { void LocalValueNumbering::RemoveSFieldsForType(uint16_t type) { // Erase all static fields of this type from the sfield_value_map_. for (auto it = sfield_value_map_.begin(), end = sfield_value_map_.end(); it != end; ) { - if (gvn_->GetFieldType(it->first) == type) { + if (gvn_->GetSFieldType(it->first) == type) { it = sfield_value_map_.erase(it); } else { ++it; @@ -1696,6 +1740,13 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } break; + case Instruction::DIV_INT: + case Instruction::DIV_INT_2ADDR: + case Instruction::REM_INT: + case Instruction::REM_INT_2ADDR: + HandleDivZeroCheck(mir, GetOperandValue(mir->ssa_rep->uses[1])); + FALLTHROUGH_INTENDED; + case Instruction::CMPG_FLOAT: case Instruction::CMPL_FLOAT: case Instruction::ADD_INT: @@ -1710,10 +1761,6 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { case Instruction::XOR_INT_2ADDR: case Instruction::SUB_INT: case Instruction::SUB_INT_2ADDR: - case Instruction::DIV_INT: - case Instruction::DIV_INT_2ADDR: - case Instruction::REM_INT: - case Instruction::REM_INT_2ADDR: case Instruction::SHL_INT: case Instruction::SHL_INT_2ADDR: case Instruction::SHR_INT: @@ -1728,19 +1775,22 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) { } break; + case Instruction::DIV_LONG: + case Instruction::REM_LONG: + case Instruction::DIV_LONG_2ADDR: + case Instruction::REM_LONG_2ADDR: + HandleDivZeroCheck(mir, GetOperandValueWide(mir->ssa_rep->uses[2])); + FALLTHROUGH_INTENDED; + case Instruction::ADD_LONG: case Instruction::SUB_LONG: case Instruction::MUL_LONG: - case Instruction::DIV_LONG: - case Instruction::REM_LONG: case Instruction::AND_LONG: case Instruction::OR_LONG: case Instruction::XOR_LONG: case Instruction::ADD_LONG_2ADDR: case Instruction::SUB_LONG_2ADDR: case Instruction::MUL_LONG_2ADDR: - case Instruction::DIV_LONG_2ADDR: - case Instruction::REM_LONG_2ADDR: case Instruction::AND_LONG_2ADDR: case Instruction::OR_LONG_2ADDR: case Instruction::XOR_LONG_2ADDR: diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h index 979fd5a08f..8613f97e38 100644 --- a/compiler/dex/local_value_numbering.h +++ b/compiler/dex/local_value_numbering.h @@ -47,6 +47,10 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { return null_checked_.find(value_name) != null_checked_.end(); } + bool IsValueDivZeroChecked(uint16_t value_name) const { + return div_zero_checked_.find(value_name) != div_zero_checked_.end(); + } + bool IsSregValue(uint16_t s_reg, uint16_t value_name) const { auto it = sreg_value_map_.find(s_reg); if (it != sreg_value_map_.end()) { @@ -286,6 +290,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { bool IsNonAliasingArray(uint16_t reg, uint16_t type) const; void HandleNullCheck(MIR* mir, uint16_t reg); void HandleRangeCheck(MIR* mir, uint16_t array, uint16_t index); + void HandleDivZeroCheck(MIR* mir, uint16_t reg); void HandlePutObject(MIR* mir); void HandleEscapingRef(uint16_t base); void HandleInvokeArgs(const MIR* mir, const LocalValueNumbering* mir_lvn); @@ -337,6 +342,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { void MergeNonAliasingIFieldValues(const IFieldLocToValueMap::value_type& entry, IFieldLocToValueMap::iterator hint); void MergeNullChecked(); + void MergeDivZeroChecked(); template <typename Map, Map LocalValueNumbering::*map_ptr, typename Versions> void MergeAliasingValues(const typename Map::value_type& entry, typename Map::iterator hint); @@ -371,6 +377,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { // Range check and null check elimination. RangeCheckSet range_checked_; ValueNameSet null_checked_; + ValueNameSet div_zero_checked_; // Reuse one vector for all merges to avoid leaking too much memory on the ArenaStack. ScopedArenaVector<BasicBlockId> merge_names_; diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc index 824c323b4b..f4f13f2c76 100644 --- a/compiler/dex/local_value_numbering_test.cc +++ b/compiler/dex/local_value_numbering_test.cc @@ -15,6 +15,7 @@ */ #include "compiler_internals.h" +#include "dex/mir_field_info.h" #include "global_value_numbering.h" #include "local_value_numbering.h" #include "gtest/gtest.h" @@ -28,6 +29,7 @@ class LocalValueNumberingTest : public testing::Test { uintptr_t declaring_dex_file; uint16_t declaring_field_idx; bool is_volatile; + DexMemAccessType type; }; struct SFieldDef { @@ -35,6 +37,7 @@ class LocalValueNumberingTest : public testing::Test { uintptr_t declaring_dex_file; uint16_t declaring_field_idx; bool is_volatile; + DexMemAccessType type; }; struct MIRDef { @@ -84,18 +87,21 @@ class LocalValueNumberingTest : public testing::Test { { opcode, 0u, 0u, 1, { reg }, 0, { } } #define DEF_UNIQUE_REF(opcode, reg) \ { opcode, 0u, 0u, 0, { }, 1, { reg } } // CONST_CLASS, CONST_STRING, NEW_ARRAY, ... +#define DEF_DIV_REM(opcode, result, dividend, divisor) \ + { opcode, 0u, 0u, 2, { dividend, divisor }, 1, { result } } +#define DEF_DIV_REM_WIDE(opcode, result, dividend, divisor) \ + { opcode, 0u, 0u, 4, { dividend, dividend + 1, divisor, divisor + 1 }, 2, { result, result + 1 } } void DoPrepareIFields(const IFieldDef* defs, size_t count) { cu_.mir_graph->ifield_lowering_infos_.clear(); cu_.mir_graph->ifield_lowering_infos_.reserve(count); for (size_t i = 0u; i != count; ++i) { const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx); + MirIFieldLoweringInfo field_info(def->field_idx, def->type); if (def->declaring_dex_file != 0u) { field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = 0u | // Without kFlagIsStatic. - (def->is_volatile ? MirIFieldLoweringInfo::kFlagIsVolatile : 0u); + field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); } cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); } @@ -111,15 +117,14 @@ class LocalValueNumberingTest : public testing::Test { cu_.mir_graph->sfield_lowering_infos_.reserve(count); for (size_t i = 0u; i != count; ++i) { const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx); + MirSFieldLoweringInfo field_info(def->field_idx, def->type); // Mark even unresolved fields as initialized. - field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic | - MirSFieldLoweringInfo::kFlagClassIsInitialized; + field_info.flags_ |= MirSFieldLoweringInfo::kFlagClassIsInitialized; // NOTE: MirSFieldLoweringInfo::kFlagClassIsInDexCache isn't used by LVN. if (def->declaring_dex_file != 0u) { field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ |= (def->is_volatile ? MirSFieldLoweringInfo::kFlagIsVolatile : 0u); + field_info.flags_ &= ~(def->is_volatile ? 0u : MirSFieldLoweringInfo::kFlagIsVolatile); } cu_.mir_graph->sfield_lowering_infos_.push_back(field_info); } @@ -140,12 +145,16 @@ class LocalValueNumberingTest : public testing::Test { mir->dalvikInsn.opcode = def->opcode; mir->dalvikInsn.vB = static_cast<int32_t>(def->value); mir->dalvikInsn.vB_wide = def->value; - if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) { + if (IsInstructionIGetOrIPut(def->opcode)) { ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size()); mir->meta.ifield_lowering_info = def->field_info; - } else if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) { + ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_info].MemAccessType(), + IGetOrIPutMemAccessType(def->opcode)); + } else if (IsInstructionSGetOrSPut(def->opcode)) { ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size()); mir->meta.sfield_lowering_info = def->field_info; + ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_info].MemAccessType(), + SGetOrSPutMemAccessType(def->opcode)); } mir->ssa_rep = &ssa_reps_[i]; mir->ssa_rep->num_uses = def->num_uses; @@ -177,6 +186,13 @@ class LocalValueNumberingTest : public testing::Test { } void PerformLVN() { + cu_.mir_graph->temp_.gvn.ifield_ids_ = GlobalValueNumbering::PrepareGvnFieldIds( + allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); + cu_.mir_graph->temp_.gvn.sfield_ids_ = GlobalValueNumbering::PrepareGvnFieldIds( + allocator_.get(), cu_.mir_graph->sfield_lowering_infos_); + gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), + GlobalValueNumbering::kModeLvn)); + lvn_.reset(new (allocator_.get()) LocalValueNumbering(gvn_.get(), 0u, allocator_.get())); value_names_.resize(mir_count_); for (size_t i = 0; i != mir_count_; ++i) { value_names_[i] = lvn_->GetValueNumber(&mirs_[i]); @@ -196,9 +212,6 @@ class LocalValueNumberingTest : public testing::Test { value_names_() { cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); - gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get(), - GlobalValueNumbering::kModeLvn)); - lvn_.reset(new (allocator_.get()) LocalValueNumbering(gvn_.get(), 0u, allocator_.get())); } ArenaPool pool_; @@ -214,7 +227,7 @@ class LocalValueNumberingTest : public testing::Test { TEST_F(LocalValueNumberingTest, IGetIGetInvokeIGet) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_IGET(Instruction::IGET, 0u, 10u, 0u), @@ -237,8 +250,8 @@ TEST_F(LocalValueNumberingTest, IGetIGetInvokeIGet) { TEST_F(LocalValueNumberingTest, IGetIPutIGetIGetIGet) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, - { 2u, 1u, 2u, false }, + { 1u, 1u, 1u, false, kDexMemAccessObject }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_IGET(Instruction::IGET_OBJECT, 0u, 10u, 0u), @@ -262,7 +275,7 @@ TEST_F(LocalValueNumberingTest, IGetIPutIGetIGetIGet) { TEST_F(LocalValueNumberingTest, UniquePreserve1) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), @@ -284,7 +297,7 @@ TEST_F(LocalValueNumberingTest, UniquePreserve1) { TEST_F(LocalValueNumberingTest, UniquePreserve2) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 11u), @@ -306,7 +319,7 @@ TEST_F(LocalValueNumberingTest, UniquePreserve2) { TEST_F(LocalValueNumberingTest, UniquePreserveAndEscape) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 10u), @@ -331,8 +344,8 @@ TEST_F(LocalValueNumberingTest, UniquePreserveAndEscape) { TEST_F(LocalValueNumberingTest, Volatile) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, - { 2u, 1u, 2u, true }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, true, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_IGET(Instruction::IGET, 0u, 10u, 1u), // Volatile. @@ -358,9 +371,9 @@ TEST_F(LocalValueNumberingTest, Volatile) { TEST_F(LocalValueNumberingTest, UnresolvedIField) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, // Resolved field #1. - { 2u, 1u, 2u, false }, // Resolved field #2. - { 3u, 0u, 0u, false }, // Unresolved field. + { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. + { 2u, 1u, 2u, false, kDexMemAccessWide }, // Resolved field #2. + { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 20u), @@ -407,9 +420,9 @@ TEST_F(LocalValueNumberingTest, UnresolvedIField) { TEST_F(LocalValueNumberingTest, UnresolvedSField) { static const SFieldDef sfields[] = { - { 1u, 1u, 1u, false }, // Resolved field #1. - { 2u, 1u, 2u, false }, // Resolved field #2. - { 3u, 0u, 0u, false }, // Unresolved field. + { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. + { 2u, 1u, 2u, false, kDexMemAccessWide }, // Resolved field #2. + { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. }; static const MIRDef mirs[] = { DEF_SGET(Instruction::SGET, 0u, 0u), // Resolved field #1. @@ -438,11 +451,11 @@ TEST_F(LocalValueNumberingTest, UnresolvedSField) { TEST_F(LocalValueNumberingTest, UninitializedSField) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, // Resolved field #1. + { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. }; static const SFieldDef sfields[] = { - { 1u, 1u, 1u, false }, // Resolved field #1. - { 2u, 1u, 2u, false }, // Resolved field #2; uninitialized. + { 1u, 1u, 1u, false, kDexMemAccessWord }, // Resolved field #1. + { 2u, 1u, 2u, false, kDexMemAccessWord }, // Resolved field #2; uninitialized. }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 200u), @@ -487,11 +500,11 @@ TEST_F(LocalValueNumberingTest, ConstString) { TEST_F(LocalValueNumberingTest, SameValueInDifferentMemoryLocations) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, - { 2u, 1u, 2u, false }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, }; static const SFieldDef sfields[] = { - { 3u, 1u, 3u, false }, + { 3u, 1u, 3u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_ARRAY, 201u), @@ -551,12 +564,12 @@ TEST_F(LocalValueNumberingTest, UniqueArrayAliasing) { TEST_F(LocalValueNumberingTest, EscapingRefs) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, // Field #1. - { 2u, 1u, 2u, false }, // Field #2. - { 3u, 1u, 3u, false }, // Reference field for storing escaping refs. - { 4u, 1u, 4u, false }, // Wide. - { 5u, 0u, 0u, false }, // Unresolved field, int. - { 6u, 0u, 0u, false }, // Unresolved field, wide. + { 1u, 1u, 1u, false, kDexMemAccessWord }, // Field #1. + { 2u, 1u, 2u, false, kDexMemAccessWord }, // Field #2. + { 3u, 1u, 3u, false, kDexMemAccessObject }, // For storing escaping refs. + { 4u, 1u, 4u, false, kDexMemAccessWide }, // Wide. + { 5u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field, int. + { 6u, 0u, 0u, false, kDexMemAccessWide }, // Unresolved field, wide. }; static const MIRDef mirs[] = { DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 20u), @@ -634,11 +647,11 @@ TEST_F(LocalValueNumberingTest, EscapingArrayRefs) { TEST_F(LocalValueNumberingTest, StoringSameValueKeepsMemoryVersion) { static const IFieldDef ifields[] = { - { 1u, 1u, 1u, false }, - { 2u, 1u, 2u, false }, + { 1u, 1u, 1u, false, kDexMemAccessWord }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, }; static const SFieldDef sfields[] = { - { 2u, 1u, 2u, false }, + { 2u, 1u, 2u, false, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_IGET(Instruction::IGET, 0u, 30u, 0u), @@ -716,8 +729,8 @@ TEST_F(LocalValueNumberingTest, FilledNewArrayTracking) { TEST_F(LocalValueNumberingTest, ClInitOnSget) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, false }, - { 1u, 2u, 1u, false }, + { 0u, 1u, 0u, false, kDexMemAccessObject }, + { 1u, 2u, 1u, false, kDexMemAccessObject }, }; static const MIRDef mirs[] = { DEF_SGET(Instruction::SGET_OBJECT, 0u, 0u), @@ -735,4 +748,26 @@ TEST_F(LocalValueNumberingTest, ClInitOnSget) { EXPECT_NE(value_names_[0], value_names_[3]); } +TEST_F(LocalValueNumberingTest, DivZeroCheck) { + static const MIRDef mirs[] = { + DEF_DIV_REM(Instruction::DIV_INT, 1u, 10u, 20u), + DEF_DIV_REM(Instruction::DIV_INT, 2u, 20u, 20u), + DEF_DIV_REM(Instruction::DIV_INT_2ADDR, 3u, 10u, 1u), + DEF_DIV_REM(Instruction::REM_INT, 4u, 30u, 20u), + DEF_DIV_REM_WIDE(Instruction::REM_LONG, 5u, 12u, 14u), + DEF_DIV_REM_WIDE(Instruction::DIV_LONG_2ADDR, 7u, 16u, 14u), + }; + + static const bool expected_ignore_div_zero_check[] = { + false, true, false, true, false, true, + }; + + PrepareMIRs(mirs); + PerformLVN(); + for (size_t i = 0u; i != mir_count_; ++i) { + int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; + EXPECT_EQ(expected, mirs_[i].optimization_flags) << i; + } +} + } // namespace art diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index 44f69ba674..7b53b14909 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -21,6 +21,7 @@ #include "dataflow_iterator-inl.h" #include "dex_instruction.h" #include "dex_instruction-inl.h" +#include "dex/mir_field_info.h" #include "dex/verified_method.h" #include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/dex_file_to_method_inliner_map.h" @@ -1204,6 +1205,8 @@ void MIRGraph::DoCacheFieldLoweringInfo() { ScopedArenaAllocator allocator(&cu_->arena_stack); uint16_t* field_idxs = reinterpret_cast<uint16_t*>(allocator.Alloc(max_refs * sizeof(uint16_t), kArenaAllocMisc)); + DexMemAccessType* field_types = reinterpret_cast<DexMemAccessType*>( + allocator.Alloc(max_refs * sizeof(DexMemAccessType), kArenaAllocMisc)); // Find IGET/IPUT/SGET/SPUT insns, store IGET/IPUT fields at the beginning, SGET/SPUT at the end. size_t ifield_pos = 0u; @@ -1214,38 +1217,41 @@ void MIRGraph::DoCacheFieldLoweringInfo() { continue; } for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (mir->dalvikInsn.opcode >= Instruction::IGET && - mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) { - // Get field index and try to find it among existing indexes. If found, it's usually among - // the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this - // is a linear search, it actually performs much better than map based approach. - if (mir->dalvikInsn.opcode <= Instruction::IPUT_SHORT) { - uint16_t field_idx = mir->dalvikInsn.vC; - size_t i = ifield_pos; - while (i != 0u && field_idxs[i - 1] != field_idx) { - --i; - } - if (i != 0u) { - mir->meta.ifield_lowering_info = i - 1; - } else { - mir->meta.ifield_lowering_info = ifield_pos; - field_idxs[ifield_pos++] = field_idx; - } + // Get field index and try to find it among existing indexes. If found, it's usually among + // the last few added, so we'll start the search from ifield_pos/sfield_pos. Though this + // is a linear search, it actually performs much better than map based approach. + if (IsInstructionIGetOrIPut(mir->dalvikInsn.opcode)) { + uint16_t field_idx = mir->dalvikInsn.vC; + size_t i = ifield_pos; + while (i != 0u && field_idxs[i - 1] != field_idx) { + --i; + } + if (i != 0u) { + mir->meta.ifield_lowering_info = i - 1; + DCHECK_EQ(field_types[i - 1], IGetOrIPutMemAccessType(mir->dalvikInsn.opcode)); + } else { + mir->meta.ifield_lowering_info = ifield_pos; + field_idxs[ifield_pos] = field_idx; + field_types[ifield_pos] = IGetOrIPutMemAccessType(mir->dalvikInsn.opcode); + ++ifield_pos; + } + } else if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { + uint16_t field_idx = mir->dalvikInsn.vB; + size_t i = sfield_pos; + while (i != max_refs && field_idxs[i] != field_idx) { + ++i; + } + if (i != max_refs) { + mir->meta.sfield_lowering_info = max_refs - i - 1u; + DCHECK_EQ(field_types[i], SGetOrSPutMemAccessType(mir->dalvikInsn.opcode)); } else { - uint16_t field_idx = mir->dalvikInsn.vB; - size_t i = sfield_pos; - while (i != max_refs && field_idxs[i] != field_idx) { - ++i; - } - if (i != max_refs) { - mir->meta.sfield_lowering_info = max_refs - i - 1u; - } else { - mir->meta.sfield_lowering_info = max_refs - sfield_pos; - field_idxs[--sfield_pos] = field_idx; - } + mir->meta.sfield_lowering_info = max_refs - sfield_pos; + --sfield_pos; + field_idxs[sfield_pos] = field_idx; + field_types[sfield_pos] = SGetOrSPutMemAccessType(mir->dalvikInsn.opcode); } - DCHECK_LE(ifield_pos, sfield_pos); } + DCHECK_LE(ifield_pos, sfield_pos); } } @@ -1254,7 +1260,7 @@ void MIRGraph::DoCacheFieldLoweringInfo() { DCHECK_EQ(ifield_lowering_infos_.size(), 0u); ifield_lowering_infos_.reserve(ifield_pos); for (size_t pos = 0u; pos != ifield_pos; ++pos) { - ifield_lowering_infos_.push_back(MirIFieldLoweringInfo(field_idxs[pos])); + ifield_lowering_infos_.push_back(MirIFieldLoweringInfo(field_idxs[pos], field_types[pos])); } MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), ifield_lowering_infos_.data(), ifield_pos); @@ -1266,7 +1272,7 @@ void MIRGraph::DoCacheFieldLoweringInfo() { sfield_lowering_infos_.reserve(max_refs - sfield_pos); for (size_t pos = max_refs; pos != sfield_pos;) { --pos; - sfield_lowering_infos_.push_back(MirSFieldLoweringInfo(field_idxs[pos])); + sfield_lowering_infos_.push_back(MirSFieldLoweringInfo(field_idxs[pos], field_types[pos])); } MirSFieldLoweringInfo::Resolve(cu_->compiler_driver, GetCurrentDexCompilationUnit(), sfield_lowering_infos_.data(), max_refs - sfield_pos); @@ -1329,19 +1335,10 @@ void MIRGraph::DoCacheMethodLoweringInfo() { continue; } for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (mir->dalvikInsn.opcode >= Instruction::INVOKE_VIRTUAL && - mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE_RANGE && - mir->dalvikInsn.opcode != Instruction::RETURN_VOID_BARRIER) { + if (IsInstructionInvoke(mir->dalvikInsn.opcode)) { // Decode target method index and invoke type. - uint16_t target_method_idx; - uint16_t invoke_type_idx; - if (mir->dalvikInsn.opcode <= Instruction::INVOKE_INTERFACE) { - target_method_idx = mir->dalvikInsn.vB; - invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL; - } else { - target_method_idx = mir->dalvikInsn.vB; - invoke_type_idx = mir->dalvikInsn.opcode - Instruction::INVOKE_VIRTUAL_RANGE; - } + uint16_t target_method_idx = mir->dalvikInsn.vB; + DexInvokeType invoke_type_idx = InvokeInstructionType(mir->dalvikInsn.opcode); // Find devirtualization target. // TODO: The devirt map is ordered by the dex pc here. Is there a way to get INVOKEs diff --git a/compiler/dex/mir_field_info.cc b/compiler/dex/mir_field_info.cc index 1db3b5b914..53afcad871 100644 --- a/compiler/dex/mir_field_info.cc +++ b/compiler/dex/mir_field_info.cc @@ -35,7 +35,7 @@ void MirIFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, DCHECK(field_infos != nullptr); DCHECK_NE(count, 0u); for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - MirIFieldLoweringInfo unresolved(it->field_idx_); + MirIFieldLoweringInfo unresolved(it->field_idx_, it->MemAccessType()); DCHECK_EQ(memcmp(&unresolved, &*it, sizeof(*it)), 0); } } @@ -66,6 +66,7 @@ void MirIFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, std::pair<bool, bool> fast_path = compiler_driver->IsFastInstanceField( dex_cache.Get(), referrer_class.Get(), resolved_field, field_idx); it->flags_ = 0u | // Without kFlagIsStatic. + (it->flags_ & (kMemAccessTypeMask << kBitMemAccessTypeBegin)) | (is_volatile ? kFlagIsVolatile : 0u) | (fast_path.first ? kFlagFastGet : 0u) | (fast_path.second ? kFlagFastPut : 0u); @@ -79,7 +80,7 @@ void MirSFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, DCHECK(field_infos != nullptr); DCHECK_NE(count, 0u); for (auto it = field_infos, end = field_infos + count; it != end; ++it) { - MirSFieldLoweringInfo unresolved(it->field_idx_); + MirSFieldLoweringInfo unresolved(it->field_idx_, it->MemAccessType()); // In 64-bit builds, there's padding after storage_index_, don't include it in memcmp. size_t size = OFFSETOF_MEMBER(MirSFieldLoweringInfo, storage_index_) + sizeof(it->storage_index_); @@ -114,6 +115,7 @@ void MirSFieldLoweringInfo::Resolve(CompilerDriver* compiler_driver, std::pair<bool, bool> fast_path = compiler_driver->IsFastStaticField( dex_cache.Get(), referrer_class, resolved_field, field_idx, &it->storage_index_); uint16_t flags = kFlagIsStatic | + (it->flags_ & (kMemAccessTypeMask << kBitMemAccessTypeBegin)) | (is_volatile ? kFlagIsVolatile : 0u) | (fast_path.first ? kFlagFastGet : 0u) | (fast_path.second ? kFlagFastPut : 0u); diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h index e97f7a00f5..ff427f88d0 100644 --- a/compiler/dex/mir_field_info.h +++ b/compiler/dex/mir_field_info.h @@ -20,6 +20,7 @@ #include "base/macros.h" #include "dex_file.h" #include "offsets.h" +#include "utils/dex_instruction_utils.h" namespace art { @@ -63,18 +64,27 @@ class MirFieldInfo { return (flags_ & kFlagIsVolatile) != 0u; } + DexMemAccessType MemAccessType() const { + return static_cast<DexMemAccessType>((flags_ >> kBitMemAccessTypeBegin) & kMemAccessTypeMask); + } + protected: enum { kBitIsStatic = 0, kBitIsVolatile, - kFieldInfoBitEnd + kBitMemAccessTypeBegin, + kBitMemAccessTypeEnd = kBitMemAccessTypeBegin + 3, // 3 bits for raw type. + kFieldInfoBitEnd = kBitMemAccessTypeEnd }; static constexpr uint16_t kFlagIsVolatile = 1u << kBitIsVolatile; static constexpr uint16_t kFlagIsStatic = 1u << kBitIsStatic; + static constexpr uint16_t kMemAccessTypeMask = 7u; + static_assert((1u << (kBitMemAccessTypeEnd - kBitMemAccessTypeBegin)) - 1u == kMemAccessTypeMask, + "Invalid raw type mask"); - MirFieldInfo(uint16_t field_idx, uint16_t flags) + MirFieldInfo(uint16_t field_idx, uint16_t flags, DexMemAccessType type) : field_idx_(field_idx), - flags_(flags), + flags_(flags | static_cast<uint16_t>(type) << kBitMemAccessTypeBegin), declaring_field_idx_(0u), declaring_class_idx_(0u), declaring_dex_file_(nullptr) { @@ -107,8 +117,8 @@ class MirIFieldLoweringInfo : public MirFieldInfo { LOCKS_EXCLUDED(Locks::mutator_lock_); // Construct an unresolved instance field lowering info. - explicit MirIFieldLoweringInfo(uint16_t field_idx) - : MirFieldInfo(field_idx, kFlagIsVolatile), // Without kFlagIsStatic. + explicit MirIFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type) + : MirFieldInfo(field_idx, kFlagIsVolatile, type), // Without kFlagIsStatic. field_offset_(0u) { } @@ -155,8 +165,8 @@ class MirSFieldLoweringInfo : public MirFieldInfo { LOCKS_EXCLUDED(Locks::mutator_lock_); // Construct an unresolved static field lowering info. - explicit MirSFieldLoweringInfo(uint16_t field_idx) - : MirFieldInfo(field_idx, kFlagIsVolatile | kFlagIsStatic), + explicit MirSFieldLoweringInfo(uint16_t field_idx, DexMemAccessType type) + : MirFieldInfo(field_idx, kFlagIsVolatile | kFlagIsStatic, type), field_offset_(0u), storage_index_(DexFile::kDexNoIndex) { } diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index b87ab66347..fdf01eba62 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); @@ -262,8 +258,6 @@ BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset, DCHECK(insn != orig_block->first_mir_insn); DCHECK(insn == bottom_block->first_mir_insn); DCHECK_EQ(insn->offset, bottom_block->start_offset); - DCHECK(static_cast<int>(insn->dalvikInsn.opcode) == kMirOpCheck || - !MIR::DecodedInstruction::IsPseudoMirOp(insn->dalvikInsn.opcode)); DCHECK_EQ(dex_pc_to_block_map_[insn->offset], orig_block->id); // Scan the "bottom" instructions, remapping them to the // newly created "bottom" block. @@ -919,7 +913,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff bb->first_mir_insn ? " | " : " "); for (mir = bb->first_mir_insn; mir; mir = mir->next) { int opcode = mir->dalvikInsn.opcode; - fprintf(file, " {%04x %s %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset, + fprintf(file, " {%04x %s %s %s %s %s %s %s %s %s\\l}%s\\\n", mir->offset, mir->ssa_rep ? GetDalvikDisassembly(mir) : !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? Instruction::Name(mir->dalvikInsn.opcode) : @@ -931,6 +925,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff (mir->optimization_flags & MIR_CALLEE) != 0 ? " inlined" : " ", (mir->optimization_flags & MIR_CLASS_IS_INITIALIZED) != 0 ? " cl_inited" : " ", (mir->optimization_flags & MIR_CLASS_IS_IN_DEX_CACHE) != 0 ? " cl_in_cache" : " ", + (mir->optimization_flags & MIR_IGNORE_DIV_ZERO_CHECK) != 0 ? " no_div_check" : " ", mir->next ? " | " : " "); } fprintf(file, " }\"];\n\n"); @@ -1173,6 +1168,14 @@ bool BasicBlock::RemoveMIRList(MIR* first_list_mir, MIR* last_list_mir) { return true; } +MIR* BasicBlock::GetFirstNonPhiInsn() { + MIR* mir = first_mir_insn; + while (mir != nullptr && static_cast<int>(mir->dalvikInsn.opcode) == kMirOpPhi) { + mir = mir->next; + } + return mir; +} + MIR* BasicBlock::GetNextUnconditionalMir(MIRGraph* mir_graph, MIR* current) { MIR* next_mir = nullptr; @@ -1213,6 +1216,10 @@ void MIRGraph::DisassembleExtendedInstr(const MIR* mir, std::string* decoded_mir int defs = (ssa_rep != nullptr) ? ssa_rep->num_defs : 0; int uses = (ssa_rep != nullptr) ? ssa_rep->num_uses : 0; + if (opcode < kMirOpFirst) { + return; // It is not an extended instruction. + } + decoded_mir->append(extended_mir_op_names_[opcode - kMirOpFirst]); switch (opcode) { @@ -1348,9 +1355,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 +1689,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 +1700,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..c8ea972d9d 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -444,6 +444,11 @@ class BasicBlock : public DeletableArenaObject<kArenaAllocBB> { void UpdatePredecessor(BasicBlockId old_pred, BasicBlockId new_pred); /** + * @brief Return first non-Phi insn. + */ + MIR* GetFirstNonPhiInsn(); + + /** * @brief Used to obtain the next MIR that follows unconditionally. * @details The implementation does not guarantee that a MIR does not * follow even if this method returns nullptr. @@ -661,13 +666,29 @@ class MIRGraph { void DoCacheFieldLoweringInfo(); const MirIFieldLoweringInfo& GetIFieldLoweringInfo(MIR* mir) const { - DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.size()); - return ifield_lowering_infos_[mir->meta.ifield_lowering_info]; + return GetIFieldLoweringInfo(mir->meta.ifield_lowering_info); + } + + const MirIFieldLoweringInfo& GetIFieldLoweringInfo(uint32_t lowering_info) const { + DCHECK_LT(lowering_info, ifield_lowering_infos_.size()); + return ifield_lowering_infos_[lowering_info]; + } + + size_t GetIFieldLoweringInfoCount() const { + return ifield_lowering_infos_.size(); } const MirSFieldLoweringInfo& GetSFieldLoweringInfo(MIR* mir) const { - DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.size()); - return sfield_lowering_infos_[mir->meta.sfield_lowering_info]; + return GetSFieldLoweringInfo(mir->meta.sfield_lowering_info); + } + + const MirSFieldLoweringInfo& GetSFieldLoweringInfo(uint32_t lowering_info) const { + DCHECK_LT(lowering_info, sfield_lowering_infos_.size()); + return sfield_lowering_infos_[lowering_info]; + } + + size_t GetSFieldLoweringInfoCount() const { + return sfield_lowering_infos_.size(); } void DoCacheMethodLoweringInfo(); @@ -1035,6 +1056,21 @@ class MIRGraph { bool ApplyGlobalValueNumberingGate(); bool ApplyGlobalValueNumbering(BasicBlock* bb); void ApplyGlobalValueNumberingEnd(); + + uint16_t GetGvnIFieldId(MIR* mir) const { + DCHECK(IsInstructionIGetOrIPut(mir->dalvikInsn.opcode)); + DCHECK_LT(mir->meta.ifield_lowering_info, ifield_lowering_infos_.size()); + DCHECK(temp_.gvn.ifield_ids_ != nullptr); + return temp_.gvn.ifield_ids_[mir->meta.ifield_lowering_info]; + } + + uint16_t GetGvnSFieldId(MIR* mir) const { + DCHECK(IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)); + DCHECK_LT(mir->meta.sfield_lowering_info, sfield_lowering_infos_.size()); + DCHECK(temp_.gvn.sfield_ids_ != nullptr); + return temp_.gvn.sfield_ids_[mir->meta.sfield_lowering_info]; + } + /* * Type inference handling helpers. Because Dalvik's bytecode is not fully typed, * we have to do some work to figure out the sreg type. For some operations it is @@ -1270,15 +1306,40 @@ 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; + uint16_t* ifield_ids_; // Part of GVN/LVN but cached here for LVN to avoid recalculation. + uint16_t* sfield_ids_; // Ditto. + } 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..1f630f78bb 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -19,6 +19,7 @@ #include "dataflow_iterator-inl.h" #include "global_value_numbering.h" #include "local_value_numbering.h" +#include "mir_field_info.h" #include "quick/dex_file_method_inliner.h" #include "quick/dex_file_to_method_inliner_map.h" #include "stack.h" @@ -217,10 +218,6 @@ static constexpr ConditionCode kIfCcZConditionCodes[] = { static_assert(arraysize(kIfCcZConditionCodes) == Instruction::IF_LEZ - Instruction::IF_EQZ + 1, "if_ccz_ccodes_size1"); -static constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) { - return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ; -} - static constexpr ConditionCode ConditionCodeForIfCcZ(Instruction::Code opcode) { return kIfCcZConditionCodes[opcode - Instruction::IF_EQZ]; } @@ -480,29 +477,25 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) { } } break; - case Instruction::GOTO: - case Instruction::GOTO_16: - case Instruction::GOTO_32: - case Instruction::IF_EQ: - case Instruction::IF_NE: - case Instruction::IF_LT: - case Instruction::IF_GE: - case Instruction::IF_GT: - case Instruction::IF_LE: - case Instruction::IF_EQZ: - case Instruction::IF_NEZ: - case Instruction::IF_LTZ: - case Instruction::IF_GEZ: - case Instruction::IF_GTZ: - case Instruction::IF_LEZ: - // If we've got a backwards branch to return, no need to suspend check. - if ((IsBackedge(bb, bb->taken) && GetBasicBlock(bb->taken)->dominates_return) || - (IsBackedge(bb, bb->fall_through) && - GetBasicBlock(bb->fall_through)->dominates_return)) { - mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK; - if (cu_->verbose) { - LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex - << mir->offset; + case Instruction::RETURN_VOID: + case Instruction::RETURN: + case Instruction::RETURN_WIDE: + case Instruction::RETURN_OBJECT: + if (bb->GetFirstNonPhiInsn() == mir) { + // This is a simple return BB. Eliminate suspend checks on predecessor back-edges. + for (BasicBlockId pred_id : bb->predecessors) { + BasicBlock* pred_bb = GetBasicBlock(pred_id); + DCHECK(pred_bb != nullptr); + if (IsBackedge(pred_bb, bb->id) && pred_bb->last_mir_insn != nullptr && + (IsInstructionIfCc(pred_bb->last_mir_insn->dalvikInsn.opcode) || + IsInstructionIfCcZ(pred_bb->last_mir_insn->dalvikInsn.opcode) || + IsInstructionGoto(pred_bb->last_mir_insn->dalvikInsn.opcode))) { + pred_bb->last_mir_insn->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK; + if (cu_->verbose) { + LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex + << pred_bb->last_mir_insn->offset; + } + } } } break; @@ -801,17 +794,18 @@ void MIRGraph::CombineBlocks(class BasicBlock* bb) { BasicBlock* bb_next = GetBasicBlock(bb->fall_through); DCHECK(!bb_next->catch_entry); DCHECK_EQ(bb_next->predecessors.size(), 1u); - // Overwrite the kMirOpCheck insn with the paired opcode. + + // Now move instructions from bb_next to bb. Start off with doing a sanity check + // that kMirOpCheck's throw instruction is first one in the bb_next. DCHECK_EQ(bb_next->first_mir_insn, throw_insn); - *bb->last_mir_insn = *throw_insn; - // And grab the rest of the instructions from bb_next. - bb->last_mir_insn = bb_next->last_mir_insn; - throw_insn->next = nullptr; - bb_next->last_mir_insn = throw_insn; - // Mark acquired instructions as belonging to bb. - for (MIR* insn = mir; insn != nullptr; insn = insn->next) { - insn->bb = bb->id; - } + // Now move all instructions (throw instruction to last one) from bb_next to bb. + MIR* last_to_move = bb_next->last_mir_insn; + bb_next->RemoveMIRList(throw_insn, last_to_move); + bb->InsertMIRListAfter(bb->last_mir_insn, throw_insn, last_to_move); + // The kMirOpCheck instruction is not needed anymore. + mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); + bb->RemoveMIR(mir); + // Before we overwrite successors, remove their predecessor links to bb. bb_next->ErasePredecessor(bb->id); if (bb->taken != NullBasicBlockId) { @@ -891,12 +885,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 = GetNumOfCodeAndTempVRs(); + 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 +913,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 +934,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 +956,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); @@ -979,7 +973,10 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { uint64_t df_attributes = GetDataFlowAttributes(mir); - DCHECK_EQ(df_attributes & DF_NULL_TRANSFER_N, 0u); // No Phis yet. + if ((df_attributes & DF_NULL_TRANSFER_N) != 0u) { + // The algorithm was written in a phi agnostic way. + continue; + } // Might need a null check? if (df_attributes & DF_HAS_NULL_CHKS) { @@ -1057,27 +1054,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 +1121,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; { @@ -1159,8 +1156,7 @@ bool MIRGraph::EliminateClassInitChecksGate() { for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { if (bb->block_type == kDalvikByteCode) { for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { - if (mir->dalvikInsn.opcode >= Instruction::SGET && - mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) { + if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir); if (!field_info.IsReferrersClass()) { DCHECK_LT(class_to_index_map.size(), 0xffffu); @@ -1173,11 +1169,10 @@ 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) { + } else if (IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { const MirMethodLoweringInfo& method_info = GetMethodLoweringInfo(mir); DCHECK(method_info.IsStatic()); if (method_info.FastPath() && !method_info.IsReferrersClass()) { @@ -1187,8 +1182,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 +1194,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 +1224,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 +1248,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; @@ -1261,12 +1256,10 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { // NOTE: index != 0xffff does not guarantee that this is an SGET/SPUT/INVOKE_STATIC. // Dex instructions with width 1 can have the same offset/2. - if (mir->dalvikInsn.opcode >= Instruction::SGET && - mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) { + if (IsInstructionSGetOrSPut(mir->dalvikInsn.opcode)) { check_initialization = true; check_dex_cache = true; - } else if (mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC || - mir->dalvikInsn.opcode == Instruction::INVOKE_STATIC_RANGE) { + } else if (IsInstructionInvokeStatic(mir->dalvikInsn.opcode)) { check_initialization = true; // NOTE: INVOKE_STATIC doesn't guarantee that the type will be in the dex cache. } @@ -1299,29 +1292,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 +1326,43 @@ 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)); + temp_.gvn.ifield_ids_ = + GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), ifield_lowering_infos_); + temp_.gvn.sfield_ids_ = + GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_); + 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 +1373,18 @@ 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; + temp_.gvn.ifield_ids_ = nullptr; + temp_.gvn.sfield_ids_ = 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; } @@ -1396,14 +1395,15 @@ void MIRGraph::ComputeInlineIFieldLoweringInfo(uint16_t field_idx, MIR* invoke, cu_, cu_->class_loader, cu_->class_linker, *target.dex_file, nullptr /* code_item not used */, 0u /* class_def_idx not used */, target.dex_method_index, 0u /* access_flags not used */, nullptr /* verified_method not used */); - MirIFieldLoweringInfo inlined_field_info(field_idx); + DexMemAccessType type = IGetOrIPutMemAccessType(iget_or_iput->dalvikInsn.opcode); + MirIFieldLoweringInfo inlined_field_info(field_idx, type); MirIFieldLoweringInfo::Resolve(cu_->compiler_driver, &inlined_unit, &inlined_field_info, 1u); DCHECK(inlined_field_info.IsResolved()); 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(); } @@ -1542,6 +1544,14 @@ bool MIRGraph::BuildExtendedBBList(class BasicBlock* bb) { } void MIRGraph::BasicBlockOptimization() { + if ((cu_->disable_opt & (1 << kLocalValueNumbering)) == 0) { + temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack)); + temp_.gvn.ifield_ids_ = + GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), ifield_lowering_infos_); + temp_.gvn.sfield_ids_ = + GlobalValueNumbering::PrepareGvnFieldIds(temp_scoped_alloc_.get(), sfield_lowering_infos_); + } + if ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) { ClearAllVisitedFlags(); PreOrderDfsIterator iter2(this); @@ -1558,6 +1568,11 @@ void MIRGraph::BasicBlockOptimization() { BasicBlockOpt(bb); } } + + // Clean up after LVN. + temp_.gvn.ifield_ids_ = nullptr; + temp_.gvn.sfield_ids_ = nullptr; + temp_scoped_alloc_.reset(); } } // namespace art diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc index 8874fafa0f..c794cc61f8 100644 --- a/compiler/dex/mir_optimization_test.cc +++ b/compiler/dex/mir_optimization_test.cc @@ -19,6 +19,7 @@ #include "compiler_internals.h" #include "dataflow_iterator.h" #include "dataflow_iterator-inl.h" +#include "dex/mir_field_info.h" #include "gtest/gtest.h" namespace art { @@ -236,15 +237,17 @@ class MirOptimizationTest : public testing::Test { ASSERT_LT(def->bbid, cu_.mir_graph->block_list_.size()); BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid]; bb->AppendMIR(mir); - if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) { - ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size()); - mir->meta.sfield_lowering_info = def->field_or_method_info; - } else if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) { + if (IsInstructionIGetOrIPut(def->opcode)) { ASSERT_LT(def->field_or_method_info, cu_.mir_graph->ifield_lowering_infos_.size()); mir->meta.ifield_lowering_info = def->field_or_method_info; - } else if (def->opcode >= Instruction::INVOKE_VIRTUAL && - def->opcode < Instruction::INVOKE_INTERFACE_RANGE && - def->opcode != Instruction::RETURN_VOID_BARRIER) { + ASSERT_EQ(cu_.mir_graph->ifield_lowering_infos_[def->field_or_method_info].MemAccessType(), + IGetOrIPutMemAccessType(def->opcode)); + } else if (IsInstructionSGetOrSPut(def->opcode)) { + ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size()); + mir->meta.sfield_lowering_info = def->field_or_method_info; + ASSERT_EQ(cu_.mir_graph->sfield_lowering_infos_[def->field_or_method_info].MemAccessType(), + SGetOrSPutMemAccessType(def->opcode)); + } else if (IsInstructionInvoke(def->opcode)) { ASSERT_LT(def->field_or_method_info, cu_.mir_graph->method_lowering_infos_.size()); mir->meta.method_lowering_info = def->field_or_method_info; } @@ -294,6 +297,7 @@ class ClassInitCheckEliminationTest : public MirOptimizationTest { uintptr_t declaring_dex_file; uint16_t declaring_class_idx; uint16_t declaring_field_idx; + DexMemAccessType type; }; void DoPrepareSFields(const SFieldDef* defs, size_t count) { @@ -301,12 +305,12 @@ class ClassInitCheckEliminationTest : public MirOptimizationTest { cu_.mir_graph->sfield_lowering_infos_.reserve(count); for (size_t i = 0u; i != count; ++i) { const SFieldDef* def = &defs[i]; - MirSFieldLoweringInfo field_info(def->field_idx); + MirSFieldLoweringInfo field_info(def->field_idx, def->type); if (def->declaring_dex_file != 0u) { field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); field_info.declaring_class_idx_ = def->declaring_class_idx; field_info.declaring_field_idx_ = def->declaring_field_idx; - field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic; + // We don't care about the volatile flag in these tests. } ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); ASSERT_FALSE(field_info.IsClassInitialized()); @@ -343,6 +347,7 @@ class NullCheckEliminationTest : public MirOptimizationTest { uintptr_t declaring_dex_file; uint16_t declaring_class_idx; uint16_t declaring_field_idx; + DexMemAccessType type; }; void DoPrepareIFields(const IFieldDef* defs, size_t count) { @@ -350,11 +355,12 @@ class NullCheckEliminationTest : public MirOptimizationTest { cu_.mir_graph->ifield_lowering_infos_.reserve(count); for (size_t i = 0u; i != count; ++i) { const IFieldDef* def = &defs[i]; - MirIFieldLoweringInfo field_info(def->field_idx); + MirIFieldLoweringInfo field_info(def->field_idx, def->type); if (def->declaring_dex_file != 0u) { field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file); field_info.declaring_class_idx_ = def->declaring_class_idx; field_info.declaring_field_idx_ = def->declaring_field_idx; + // We don't care about the volatile flag in these tests. } ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved()); cu_.mir_graph->ifield_lowering_infos_.push_back(field_info); @@ -393,12 +399,12 @@ class NullCheckEliminationTest : public MirOptimizationTest { TEST_F(ClassInitCheckEliminationTest, SingleBlock) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, - { 2u, 1u, 2u, 2u }, - { 3u, 1u, 3u, 3u }, // Same declaring class as sfield[4]. - { 4u, 1u, 3u, 4u }, // Same declaring class as sfield[3]. - { 5u, 0u, 0u, 0u }, // Unresolved. + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, + { 2u, 1u, 2u, 2u, kDexMemAccessWord }, + { 3u, 1u, 3u, 3u, kDexMemAccessWord }, // Same declaring class as sfield[4]. + { 4u, 1u, 3u, 4u, kDexMemAccessWord }, // Same declaring class as sfield[3]. + { 5u, 0u, 0u, 0u, kDexMemAccessWord }, // Unresolved. }; static const MIRDef mirs[] = { DEF_SGET_SPUT(3u, Instruction::SPUT, 0u, 5u), // Unresolved. @@ -432,9 +438,9 @@ TEST_F(ClassInitCheckEliminationTest, SingleBlock) { TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, - { 2u, 1u, 2u, 2u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, + { 2u, 1u, 2u, 2u, kDexMemAccessWord }, }; static const MethodDef methods[] = { { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, @@ -473,17 +479,17 @@ TEST_F(ClassInitCheckEliminationTest, SingleBlockWithInvokes) { TEST_F(ClassInitCheckEliminationTest, Diamond) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, - { 2u, 1u, 2u, 2u }, - { 3u, 1u, 3u, 3u }, - { 4u, 1u, 4u, 4u }, - { 5u, 1u, 5u, 5u }, - { 6u, 1u, 6u, 6u }, - { 7u, 1u, 7u, 7u }, - { 8u, 1u, 8u, 8u }, // Same declaring class as sfield[9]. - { 9u, 1u, 8u, 9u }, // Same declaring class as sfield[8]. - { 10u, 0u, 0u, 0u }, // Unresolved. + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, + { 2u, 1u, 2u, 2u, kDexMemAccessWord }, + { 3u, 1u, 3u, 3u, kDexMemAccessWord }, + { 4u, 1u, 4u, 4u, kDexMemAccessWord }, + { 5u, 1u, 5u, 5u, kDexMemAccessWord }, + { 6u, 1u, 6u, 6u, kDexMemAccessWord }, + { 7u, 1u, 7u, 7u, kDexMemAccessWord }, + { 8u, 1u, 8u, 8u, kDexMemAccessWord }, // Same declaring class as sfield[9]. + { 9u, 1u, 8u, 9u, kDexMemAccessWord }, // Same declaring class as sfield[8]. + { 10u, 0u, 0u, 0u, kDexMemAccessWord }, // Unresolved. }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -539,11 +545,11 @@ TEST_F(ClassInitCheckEliminationTest, Diamond) { TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, - { 2u, 1u, 2u, 2u }, - { 3u, 1u, 3u, 3u }, - { 4u, 1u, 4u, 4u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, + { 2u, 1u, 2u, 2u, kDexMemAccessWord }, + { 3u, 1u, 3u, 3u, kDexMemAccessWord }, + { 4u, 1u, 4u, 4u, kDexMemAccessWord }, }; static const MethodDef methods[] = { { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, @@ -600,9 +606,9 @@ TEST_F(ClassInitCheckEliminationTest, DiamondWithInvokes) { TEST_F(ClassInitCheckEliminationTest, Loop) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, - { 2u, 1u, 2u, 2u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, + { 2u, 1u, 2u, 2u, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), @@ -631,7 +637,7 @@ TEST_F(ClassInitCheckEliminationTest, Loop) { TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, }; static const MethodDef methods[] = { { 0u, 1u, 0u, 0u, kStatic, kStatic, false, false }, @@ -671,10 +677,10 @@ TEST_F(ClassInitCheckEliminationTest, LoopWithInvokes) { TEST_F(ClassInitCheckEliminationTest, Catch) { static const SFieldDef sfields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, - { 2u, 1u, 2u, 2u }, - { 3u, 1u, 3u, 3u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, + { 2u, 1u, 2u, 2u, kDexMemAccessWord }, + { 3u, 1u, 3u, 3u, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_SGET_SPUT(3u, Instruction::SGET, 0u, 0u), // Before the exception edge. @@ -707,9 +713,9 @@ TEST_F(ClassInitCheckEliminationTest, Catch) { TEST_F(NullCheckEliminationTest, SingleBlock) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 0u, 1u }, - { 2u, 1u, 0u, 2u }, // Object. + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 0u, 1u, kDexMemAccessWord }, + { 2u, 1u, 0u, 2u, kDexMemAccessObject }, }; static const MIRDef mirs[] = { DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u), @@ -768,9 +774,9 @@ TEST_F(NullCheckEliminationTest, SingleBlock) { TEST_F(NullCheckEliminationTest, Diamond) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 0u, 1u }, - { 2u, 1u, 0u, 2u }, // int[]. + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 0u, 1u, kDexMemAccessWord }, + { 2u, 1u, 0u, 2u, kDexMemAccessObject }, // int[]. }; static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. @@ -816,8 +822,8 @@ TEST_F(NullCheckEliminationTest, Diamond) { TEST_F(NullCheckEliminationTest, Loop) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), @@ -846,8 +852,8 @@ TEST_F(NullCheckEliminationTest, Loop) { TEST_F(NullCheckEliminationTest, Catch) { static const IFieldDef ifields[] = { - { 0u, 1u, 0u, 0u }, - { 1u, 1u, 1u, 1u }, + { 0u, 1u, 0u, 0u, kDexMemAccessWord }, + { 1u, 1u, 1u, 1u, kDexMemAccessWord }, }; static const MIRDef mirs[] = { DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), // Before the exception edge. diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index a3b4df3c7e..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); } 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/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 3e5b7bfe0a..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); } 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/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..4dd24cb9f6 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)); } @@ -570,6 +583,7 @@ class StaticFieldSlowPath : public Mir2Lir::LIRSlowPath { void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, OpSize size) { const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir); + DCHECK_EQ(SPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType()); cu_->compiler_driver->ProcessedStaticField(field_info.FastPut(), field_info.IsReferrersClass()); if (!SLOW_FIELD_PATH && field_info.FastPut()) { DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); @@ -688,6 +702,7 @@ void Mir2Lir::GenSput(MIR* mir, RegLocation rl_src, OpSize size) { void Mir2Lir::GenSget(MIR* mir, RegLocation rl_dest, OpSize size, Primitive::Type type) { const MirSFieldLoweringInfo& field_info = mir_graph_->GetSFieldLoweringInfo(mir); + DCHECK_EQ(SGetMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType()); cu_->compiler_driver->ProcessedStaticField(field_info.FastGet(), field_info.IsReferrersClass()); if (!SLOW_FIELD_PATH && field_info.FastGet()) { @@ -826,6 +841,7 @@ void Mir2Lir::HandleSlowPaths() { void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, Primitive::Type type, RegLocation rl_dest, RegLocation rl_obj) { const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir); + DCHECK_EQ(IGetMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType()); cu_->compiler_driver->ProcessedInstanceField(field_info.FastGet()); if (!SLOW_FIELD_PATH && field_info.FastGet()) { RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile()); @@ -899,6 +915,7 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, Primitive::Type type void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, RegLocation rl_src, RegLocation rl_obj) { const MirIFieldLoweringInfo& field_info = mir_graph_->GetIFieldLoweringInfo(mir); + DCHECK_EQ(IPutMemAccessType(mir->dalvikInsn.opcode), field_info.MemAccessType()); cu_->compiler_driver->ProcessedInstanceField(field_info.FastPut()); if (!SLOW_FIELD_PATH && field_info.FastPut()) { RegisterClass reg_class = RegClassForFieldLoadStore(size, field_info.IsVolatile()); 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.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/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 3933b21a26..84d68d2b7b 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -424,15 +424,15 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86PextrbRRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0x66, 0, 0x0F, 0x3A, 0x14, 0, 0, 1, false }, "PextbRRI", "!0r,!1r,!2d" }, { kX86PextrwRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0x66, 0, 0x0F, 0xC5, 0x00, 0, 0, 1, false }, "PextwRRI", "!0r,!1r,!2d" }, { kX86PextrdRRI, kRegRegImmStore, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "PextdRRI", "!0r,!1r,!2d" }, - { kX86PextrbMRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_STORE, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "kX86PextrbMRI", "[!0r+!1d],!2r,!3d" }, - { kX86PextrwMRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_STORE, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "kX86PextrwMRI", "[!0r+!1d],!2r,!3d" }, - { kX86PextrdMRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_STORE, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "kX86PextrdMRI", "[!0r+!1d],!2r,!3d" }, + { kX86PextrbMRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_STORE, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "PextrbMRI", "[!0r+!1d],!2r,!3d" }, + { kX86PextrwMRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_STORE, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "PextrwMRI", "[!0r+!1d],!2r,!3d" }, + { kX86PextrdMRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_STORE, { 0x66, 0, 0x0F, 0x3A, 0x16, 0, 0, 1, false }, "PextrdMRI", "[!0r+!1d],!2r,!3d" }, { kX86PshuflwRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0xF2, 0, 0x0F, 0x70, 0, 0, 0, 1, false }, "PshuflwRRI", "!0r,!1r,!2d" }, { kX86PshufdRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0x66, 0, 0x0F, 0x70, 0, 0, 0, 1, false }, "PshuffRRI", "!0r,!1r,!2d" }, - { kX86ShufpsRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0x00, 0, 0x0F, 0xC6, 0, 0, 0, 1, false }, "kX86ShufpsRRI", "!0r,!1r,!2d" }, - { kX86ShufpdRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0 | REG_USE1, { 0x66, 0, 0x0F, 0xC6, 0, 0, 0, 1, false }, "kX86ShufpdRRI", "!0r,!1r,!2d" }, + { kX86ShufpsRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0_USE0 | REG_USE1, { 0x00, 0, 0x0F, 0xC6, 0, 0, 0, 1, false }, "ShufpsRRI", "!0r,!1r,!2d" }, + { kX86ShufpdRRI, kRegRegImm, IS_TERTIARY_OP | REG_DEF0_USE0 | REG_USE1, { 0x66, 0, 0x0F, 0xC6, 0, 0, 0, 1, false }, "ShufpdRRI", "!0r,!1r,!2d" }, { kX86PsrawRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x71, 0, 4, 0, 1, false }, "PsrawRI", "!0r,!1d" }, { kX86PsradRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x72, 0, 4, 0, 1, false }, "PsradRI", "!0r,!1d" }, 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 ead31b37b6..998aeff368 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -2263,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/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/dex/verified_method.cc b/compiler/dex/verified_method.cc index 9f0a696096..17328c4a5b 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -282,6 +282,10 @@ void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifi Instruction::Code code = inst->Opcode(); if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) { uint32_t dex_pc = inst->GetDexPc(code_item->insns_); + if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) { + // Do not attempt to quicken this instruction, it's unreachable anyway. + continue; + } const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc); bool is_safe_cast = false; if (code == Instruction::CHECK_CAST) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 08041e8f24..2e9f8355f2 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1986,6 +1986,7 @@ static bool InstructionSetHasGenericJniStub(InstructionSet isa) { case kArm: case kArm64: case kThumb2: + case kMips: case kX86: case kX86_64: return true; default: return false; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index be8631ad42..b261460690 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -305,6 +305,15 @@ void HGraphBuilder::Binop_23x(const Instruction& instruction, } template<typename T> +void HGraphBuilder::Binop_23x_shift(const Instruction& instruction, + Primitive::Type type) { + HInstruction* first = LoadLocal(instruction.VRegB(), type); + HInstruction* second = LoadLocal(instruction.VRegC(), Primitive::kPrimInt); + current_block_->AddInstruction(new (arena_) T(type, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type type) { HInstruction* first = LoadLocal(instruction.VRegA(), type); HInstruction* second = LoadLocal(instruction.VRegB(), type); @@ -313,6 +322,14 @@ void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type ty } template<typename T> +void HGraphBuilder::Binop_12x_shift(const Instruction& instruction, Primitive::Type type) { + HInstruction* first = LoadLocal(instruction.VRegA(), type); + HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); + current_block_->AddInstruction(new (arena_) T(type, first, second)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); +} + +template<typename T> void HGraphBuilder::Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc) { @@ -1141,6 +1158,36 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SHL_INT: { + Binop_23x_shift<HShl>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::SHL_LONG: { + Binop_23x_shift<HShl>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::SHR_INT: { + Binop_23x_shift<HShr>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::SHR_LONG: { + Binop_23x_shift<HShr>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::USHR_INT: { + Binop_23x_shift<HUShr>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::USHR_LONG: { + Binop_23x_shift<HUShr>(instruction, Primitive::kPrimLong); + break; + } + case Instruction::OR_INT: { Binop_23x<HOr>(instruction, Primitive::kPrimInt); break; @@ -1240,6 +1287,36 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SHL_INT_2ADDR: { + Binop_12x_shift<HShl>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::SHL_LONG_2ADDR: { + Binop_12x_shift<HShl>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::SHR_INT_2ADDR: { + Binop_12x_shift<HShr>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::SHR_LONG_2ADDR: { + Binop_12x_shift<HShr>(instruction, Primitive::kPrimLong); + break; + } + + case Instruction::USHR_INT_2ADDR: { + Binop_12x_shift<HUShr>(instruction, Primitive::kPrimInt); + break; + } + + case Instruction::USHR_LONG_2ADDR: { + Binop_12x_shift<HUShr>(instruction, Primitive::kPrimLong); + break; + } + case Instruction::DIV_FLOAT_2ADDR: { Binop_12x<HDiv>(instruction, Primitive::kPrimFloat, dex_pc); break; @@ -1354,6 +1431,21 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::SHL_INT_LIT8: { + Binop_22b<HShl>(instruction, false); + break; + } + + case Instruction::SHR_INT_LIT8: { + Binop_22b<HShr>(instruction, false); + break; + } + + case Instruction::USHR_INT_LIT8: { + Binop_22b<HUShr>(instruction, false); + break; + } + case Instruction::NEW_INSTANCE: { current_block_->AddInstruction( new (arena_) HNewInstance(dex_pc, instruction.VRegB_21c())); diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 897bcece7b..204005daa6 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -105,12 +105,18 @@ class HGraphBuilder : public ValueObject { void Binop_23x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc); template<typename T> + void Binop_23x_shift(const Instruction& instruction, Primitive::Type type); + + template<typename T> void Binop_12x(const Instruction& instruction, Primitive::Type type); template<typename T> void Binop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc); template<typename T> + void Binop_12x_shift(const Instruction& instruction, Primitive::Type type); + + template<typename T> void Binop_22b(const Instruction& instruction, bool reverse); template<typename T> diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 1701ef5f0a..a204e21495 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2087,6 +2087,124 @@ void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) } } +void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { + DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); + + LocationSummary::CallKind call_kind = op->GetResultType() == Primitive::kPrimLong + ? LocationSummary::kCall + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(op, call_kind); + + switch (op->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(op->InputAt(1))); + locations->SetOut(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::RegisterLocation(calling_convention.GetRegisterAt(2))); + // The runtime helper puts the output in R0,R2. + locations->SetOut(Location::RegisterPairLocation(R0, R2)); + break; + } + default: + LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); + } +} + +void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { + DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); + + LocationSummary* locations = op->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + + Primitive::Type type = op->GetResultType(); + switch (type) { + case Primitive::kPrimInt: { + Register out_reg = out.As<Register>(); + Register first_reg = first.As<Register>(); + // Arm doesn't mask the shift count so we need to do it ourselves. + if (second.IsRegister()) { + Register second_reg = second.As<Register>(); + __ and_(second_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); + if (op->IsShl()) { + __ Lsl(out_reg, first_reg, second_reg); + } else if (op->IsShr()) { + __ Asr(out_reg, first_reg, second_reg); + } else { + __ Lsr(out_reg, first_reg, second_reg); + } + } else { + int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); + uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); + if (shift_value == 0) { // arm does not support shifting with 0 immediate. + __ Mov(out_reg, first_reg); + } else if (op->IsShl()) { + __ Lsl(out_reg, first_reg, shift_value); + } else if (op->IsShr()) { + __ Asr(out_reg, first_reg, shift_value); + } else { + __ Lsr(out_reg, first_reg, shift_value); + } + } + break; + } + case Primitive::kPrimLong: { + // TODO: Inline the assembly instead of calling the runtime. + 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.As<Register>()); + DCHECK_EQ(R0, out.AsRegisterPairLow<Register>()); + DCHECK_EQ(R2, out.AsRegisterPairHigh<Register>()); + + int32_t entry_point_offset; + if (op->IsShl()) { + entry_point_offset = QUICK_ENTRY_POINT(pShlLong); + } else if (op->IsShr()) { + entry_point_offset = QUICK_ENTRY_POINT(pShrLong); + } else { + entry_point_offset = QUICK_ENTRY_POINT(pUshrLong); + } + __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset); + __ blx(LR); + break; + } + default: + LOG(FATAL) << "Unexpected operation type " << type; + } +} + +void LocationsBuilderARM::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void InstructionCodeGeneratorARM::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void LocationsBuilderARM::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void InstructionCodeGeneratorARM::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void LocationsBuilderARM::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + +void InstructionCodeGeneratorARM::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index c00fac1a37..226e635d05 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -109,6 +109,7 @@ class LocationsBuilderARM : public HGraphVisitor { private: void HandleInvoke(HInvoke* invoke); void HandleBitwiseOperation(HBinaryOperation* operation); + void HandleShift(HBinaryOperation* operation); CodeGeneratorARM* const codegen_; InvokeDexCallingConventionVisitor parameter_visitor_; @@ -136,6 +137,7 @@ class InstructionCodeGeneratorARM : public HGraphVisitor { void GenerateSuspendCheck(HSuspendCheck* check, HBasicBlock* successor); void GenerateClassInitializationCheck(SlowPathCodeARM* slow_path, Register class_reg); void HandleBitwiseOperation(HBinaryOperation* operation); + void HandleShift(HBinaryOperation* operation); ArmAssembler* const assembler_; CodeGeneratorARM* const codegen_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 82dced5e4f..7a8b941c22 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -801,7 +801,10 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph, #define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \ M(ParallelMove) \ - M(Rem) + M(Rem) \ + M(Shl) \ + M(Shr) \ + M(UShr) \ #define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 3c53cea0bf..917b7dd2bb 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2129,6 +2129,139 @@ void InstructionCodeGeneratorX86::VisitDivZeroCheck(HDivZeroCheck* instruction) } } +void LocationsBuilderX86::HandleShift(HBinaryOperation* op) { + DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); + + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); + + switch (op->GetResultType()) { + case Primitive::kPrimInt: { + locations->SetInAt(0, Location::RequiresRegister()); + // The shift count needs to be in CL. + locations->SetInAt(1, Location::ByteRegisterOrConstant(ECX, op->InputAt(1))); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + // The shift count needs to be in CL. + locations->SetInAt(1, Location::RegisterLocation(ECX)); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + default: + LOG(FATAL) << "Unexpected op type " << op->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::HandleShift(HBinaryOperation* op) { + DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); + + LocationSummary* locations = op->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + DCHECK(first.Equals(locations->Out())); + + switch (op->GetResultType()) { + case Primitive::kPrimInt: { + Register first_reg = first.As<Register>(); + if (second.IsRegister()) { + Register second_reg = second.As<Register>(); + DCHECK_EQ(ECX, second_reg); + if (op->IsShl()) { + __ shll(first_reg, second_reg); + } else if (op->IsShr()) { + __ sarl(first_reg, second_reg); + } else { + __ shrl(first_reg, second_reg); + } + } else { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + if (op->IsShl()) { + __ shll(first_reg, imm); + } else if (op->IsShr()) { + __ sarl(first_reg, imm); + } else { + __ shrl(first_reg, imm); + } + } + break; + } + case Primitive::kPrimLong: { + Register second_reg = second.As<Register>(); + DCHECK_EQ(ECX, second_reg); + if (op->IsShl()) { + GenerateShlLong(first, second_reg); + } else if (op->IsShr()) { + GenerateShrLong(first, second_reg); + } else { + GenerateUShrLong(first, second_reg); + } + break; + } + default: + LOG(FATAL) << "Unexpected op type " << op->GetResultType(); + } +} + +void InstructionCodeGeneratorX86::GenerateShlLong(const Location& loc, Register shifter) { + Label done; + __ shld(loc.AsRegisterPairHigh<Register>(), loc.AsRegisterPairLow<Register>(), shifter); + __ shll(loc.AsRegisterPairLow<Register>(), shifter); + __ testl(shifter, Immediate(32)); + __ j(kEqual, &done); + __ movl(loc.AsRegisterPairHigh<Register>(), loc.AsRegisterPairLow<Register>()); + __ movl(loc.AsRegisterPairLow<Register>(), Immediate(0)); + __ Bind(&done); +} + +void InstructionCodeGeneratorX86::GenerateShrLong(const Location& loc, Register shifter) { + Label done; + __ shrd(loc.AsRegisterPairLow<Register>(), loc.AsRegisterPairHigh<Register>(), shifter); + __ sarl(loc.AsRegisterPairHigh<Register>(), shifter); + __ testl(shifter, Immediate(32)); + __ j(kEqual, &done); + __ movl(loc.AsRegisterPairLow<Register>(), loc.AsRegisterPairHigh<Register>()); + __ sarl(loc.AsRegisterPairHigh<Register>(), Immediate(31)); + __ Bind(&done); +} + +void InstructionCodeGeneratorX86::GenerateUShrLong(const Location& loc, Register shifter) { + Label done; + __ shrd(loc.AsRegisterPairLow<Register>(), loc.AsRegisterPairHigh<Register>(), shifter); + __ shrl(loc.AsRegisterPairHigh<Register>(), shifter); + __ testl(shifter, Immediate(32)); + __ j(kEqual, &done); + __ movl(loc.AsRegisterPairLow<Register>(), loc.AsRegisterPairHigh<Register>()); + __ movl(loc.AsRegisterPairHigh<Register>(), Immediate(0)); + __ Bind(&done); +} + +void LocationsBuilderX86::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void InstructionCodeGeneratorX86::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void LocationsBuilderX86::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void InstructionCodeGeneratorX86::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void LocationsBuilderX86::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + +void InstructionCodeGeneratorX86::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 0aff6cc493..aed06c04df 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -104,6 +104,7 @@ class LocationsBuilderX86 : public HGraphVisitor { private: void HandleBitwiseOperation(HBinaryOperation* instruction); void HandleInvoke(HInvoke* invoke); + void HandleShift(HBinaryOperation* instruction); CodeGeneratorX86* const codegen_; InvokeDexCallingConventionVisitor parameter_visitor_; @@ -132,6 +133,10 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { void GenerateClassInitializationCheck(SlowPathCodeX86* slow_path, Register class_reg); void HandleBitwiseOperation(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); + void HandleShift(HBinaryOperation* instruction); + void GenerateShlLong(const Location& loc, Register shifter); + void GenerateShrLong(const Location& loc, Register shifter); + void GenerateUShrLong(const Location& loc, Register shifter); 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 97f5e5c7ac..69f031aab1 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -2026,6 +2026,107 @@ void InstructionCodeGeneratorX86_64::VisitDivZeroCheck(HDivZeroCheck* instructio } } +void LocationsBuilderX86_64::HandleShift(HBinaryOperation* op) { + DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); + + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall); + + switch (op->GetResultType()) { + case Primitive::kPrimInt: + case Primitive::kPrimLong: { + locations->SetInAt(0, Location::RequiresRegister()); + // The shift count needs to be in CL. + locations->SetInAt(1, Location::ByteRegisterOrConstant(RCX, op->InputAt(1))); + locations->SetOut(Location::SameAsFirstInput()); + break; + } + default: + LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); + } +} + +void InstructionCodeGeneratorX86_64::HandleShift(HBinaryOperation* op) { + DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); + + LocationSummary* locations = op->GetLocations(); + CpuRegister first_reg = locations->InAt(0).As<CpuRegister>(); + Location second = locations->InAt(1); + + switch (op->GetResultType()) { + case Primitive::kPrimInt: { + if (second.IsRegister()) { + CpuRegister second_reg = second.As<CpuRegister>(); + if (op->IsShl()) { + __ shll(first_reg, second_reg); + } else if (op->IsShr()) { + __ sarl(first_reg, second_reg); + } else { + __ shrl(first_reg, second_reg); + } + } else { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + if (op->IsShl()) { + __ shll(first_reg, imm); + } else if (op->IsShr()) { + __ sarl(first_reg, imm); + } else { + __ shrl(first_reg, imm); + } + } + break; + } + case Primitive::kPrimLong: { + if (second.IsRegister()) { + CpuRegister second_reg = second.As<CpuRegister>(); + if (op->IsShl()) { + __ shlq(first_reg, second_reg); + } else if (op->IsShr()) { + __ sarq(first_reg, second_reg); + } else { + __ shrq(first_reg, second_reg); + } + } else { + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue()); + if (op->IsShl()) { + __ shlq(first_reg, imm); + } else if (op->IsShr()) { + __ sarq(first_reg, imm); + } else { + __ shrq(first_reg, imm); + } + } + break; + } + default: + LOG(FATAL) << "Unexpected operation type " << op->GetResultType(); + } +} + +void LocationsBuilderX86_64::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void InstructionCodeGeneratorX86_64::VisitShl(HShl* shl) { + HandleShift(shl); +} + +void LocationsBuilderX86_64::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void InstructionCodeGeneratorX86_64::VisitShr(HShr* shr) { + HandleShift(shr); +} + +void LocationsBuilderX86_64::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + +void InstructionCodeGeneratorX86_64::VisitUShr(HUShr* ushr) { + HandleShift(ushr); +} + void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 29c679d8f1..794b81ffbc 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -108,6 +108,7 @@ class LocationsBuilderX86_64 : public HGraphVisitor { private: void HandleInvoke(HInvoke* invoke); void HandleBitwiseOperation(HBinaryOperation* operation); + void HandleShift(HBinaryOperation* operation); CodeGeneratorX86_64* const codegen_; InvokeDexCallingConventionVisitor parameter_visitor_; @@ -136,6 +137,7 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor { void GenerateClassInitializationCheck(SlowPathCodeX86_64* slow_path, CpuRegister class_reg); void HandleBitwiseOperation(HBinaryOperation* operation); void GenerateDivRemIntegral(HBinaryOperation* instruction); + void HandleShift(HBinaryOperation* operation); X86_64Assembler* const assembler_; CodeGeneratorX86_64* const codegen_; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 7d52d7d221..b47549add5 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -42,6 +42,9 @@ static const int kDefaultNumberOfPredecessors = 2; static const int kDefaultNumberOfDominatedBlocks = 1; static const int kDefaultNumberOfBackEdges = 1; +static constexpr uint32_t kMaxIntShiftValue = 0x1f; +static constexpr uint64_t kMaxLongShiftValue = 0x3f; + enum IfCondition { kCondEQ, kCondNE, @@ -521,9 +524,11 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { M(ParallelMove, Instruction) \ M(ParameterValue, Instruction) \ M(Phi, Instruction) \ - M(Rem, BinaryOperation) \ + M(Rem, BinaryOperation) \ M(Return, Instruction) \ M(ReturnVoid, Instruction) \ + M(Shl, BinaryOperation) \ + M(Shr, BinaryOperation) \ M(StaticFieldGet, Instruction) \ M(StaticFieldSet, Instruction) \ M(StoreLocal, Instruction) \ @@ -532,6 +537,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { M(Temporary, Instruction) \ M(Throw, Instruction) \ M(TypeConversion, Instruction) \ + M(UShr, BinaryOperation) \ M(Xor, BinaryOperation) \ #define FOR_EACH_INSTRUCTION(M) \ @@ -1831,6 +1837,57 @@ class HDivZeroCheck : public HExpression<1> { DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck); }; +class HShl : public HBinaryOperation { + public: + HShl(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x << (y & kMaxIntShiftValue); } + int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x << (y & kMaxLongShiftValue); } + + DECLARE_INSTRUCTION(Shl); + + private: + DISALLOW_COPY_AND_ASSIGN(HShl); +}; + +class HShr : public HBinaryOperation { + public: + HShr(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x >> (y & kMaxIntShiftValue); } + int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x >> (y & kMaxLongShiftValue); } + + DECLARE_INSTRUCTION(Shr); + + private: + DISALLOW_COPY_AND_ASSIGN(HShr); +}; + +class HUShr : public HBinaryOperation { + public: + HUShr(Primitive::Type result_type, HInstruction* left, HInstruction* right) + : HBinaryOperation(result_type, left, right) {} + + int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { + uint32_t ux = static_cast<uint32_t>(x); + uint32_t uy = static_cast<uint32_t>(y) & kMaxIntShiftValue; + return static_cast<int32_t>(ux >> uy); + } + + int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { + uint64_t ux = static_cast<uint64_t>(x); + uint64_t uy = static_cast<uint64_t>(y) & kMaxLongShiftValue; + return static_cast<int64_t>(ux >> uy); + } + + DECLARE_INSTRUCTION(UShr); + + private: + DISALLOW_COPY_AND_ASSIGN(HUShr); +}; + class HAnd : public HBinaryOperation { public: HAnd(Primitive::Type result_type, HInstruction* left, HInstruction* right) diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index a1594b02ac..a541763881 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -1079,7 +1079,7 @@ void Arm32Assembler::EmitVFPds(Condition cond, int32_t opcode, void Arm32Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Do not use Lsl if no shift is wanted. + CHECK_LE(shift_imm, 31u); if (setcc) { movs(rd, ShifterOperand(rm, LSL, shift_imm), cond); } else { @@ -1090,7 +1090,7 @@ void Arm32Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, void Arm32Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Do not use Lsr if no shift is wanted. + CHECK(1u <= shift_imm && shift_imm <= 32u); if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. if (setcc) { movs(rd, ShifterOperand(rm, LSR, shift_imm), cond); @@ -1102,7 +1102,7 @@ void Arm32Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, void Arm32Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Do not use Asr if no shift is wanted. + CHECK(1u <= shift_imm && shift_imm <= 32u); if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. if (setcc) { movs(rd, ShifterOperand(rm, ASR, shift_imm), cond); @@ -1114,7 +1114,7 @@ void Arm32Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, void Arm32Assembler::Ror(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Use Rrx instruction. + CHECK(1u <= shift_imm && shift_imm <= 31u); if (setcc) { movs(rd, ShifterOperand(rm, ROR, shift_imm), cond); } else { diff --git a/compiler/utils/arm/assembler_arm_test.h b/compiler/utils/arm/assembler_arm_test.h index 34475b2bfd..838abb696d 100644 --- a/compiler/utils/arm/assembler_arm_test.h +++ b/compiler/utils/arm/assembler_arm_test.h @@ -159,7 +159,7 @@ class AssemblerArmTest : public AssemblerTest<Ass, Reg, FPReg, Imm> { std::string RepeatRRiiC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond), std::vector<std::pair<Imm, Imm>>& immediates, std::string fmt) { - return RepeatTemplatedRRiiC(f, GetRegisters(), GetRegisters(), + return RepeatTemplatedRRiiC<Reg, Reg>(f, GetRegisters(), GetRegisters(), &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>, immediates, fmt); diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index a34920999e..a377cb2892 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -2210,7 +2210,7 @@ void Thumb2Assembler::EmitBranches() { void Thumb2Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Do not use Lsl if no shift is wanted. + CHECK_LE(shift_imm, 31u); CheckCondition(cond); EmitShift(rd, rm, LSL, shift_imm, setcc); } @@ -2218,7 +2218,7 @@ void Thumb2Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, void Thumb2Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Do not use Lsr if no shift is wanted. + CHECK(1u <= shift_imm && shift_imm <= 32u); if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. CheckCondition(cond); EmitShift(rd, rm, LSR, shift_imm, setcc); @@ -2227,7 +2227,7 @@ void Thumb2Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, void Thumb2Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Do not use Asr if no shift is wanted. + CHECK(1u <= shift_imm && shift_imm <= 32u); if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. CheckCondition(cond); EmitShift(rd, rm, ASR, shift_imm, setcc); @@ -2236,7 +2236,7 @@ void Thumb2Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, void Thumb2Assembler::Ror(Register rd, Register rm, uint32_t shift_imm, bool setcc, Condition cond) { - CHECK_NE(shift_imm, 0u); // Use Rrx instruction. + CHECK(1u <= shift_imm && shift_imm <= 31u); CheckCondition(cond); EmitShift(rd, rm, ROR, shift_imm, setcc); } diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 1fadb916fc..2b5512077e 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -226,6 +226,10 @@ class AssemblerTest : public testing::Test { UNREACHABLE(); } + std::string GetRegisterName(const Reg& reg) { + return GetRegName<RegisterView::kUsePrimaryName>(reg); + } + protected: explicit AssemblerTest() {} diff --git a/compiler/utils/dex_instruction_utils.h b/compiler/utils/dex_instruction_utils.h new file mode 100644 index 0000000000..ad7d750dea --- /dev/null +++ b/compiler/utils/dex_instruction_utils.h @@ -0,0 +1,192 @@ +/* + * 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_DEX_INSTRUCTION_UTILS_H_ +#define ART_COMPILER_UTILS_DEX_INSTRUCTION_UTILS_H_ + +#include "dex_instruction.h" + +namespace art { + +// Dex invoke type corresponds to the ordering of INVOKE instructions; +// this order is the same for range and non-range invokes. +enum DexInvokeType : uint8_t { + kDexInvokeVirtual = 0, // invoke-virtual, invoke-virtual-range + kDexInvokeSuper, // invoke-super, invoke-super-range + kDexInvokeDirect, // invoke-direct, invoke-direct-range + kDexInvokeStatic, // invoke-static, invoke-static-range + kDexInvokeInterface, // invoke-interface, invoke-interface-range + kDexInvokeTypeCount +}; + +// Dex instruction memory access types correspond to the ordering of GET/PUT instructions; +// this order is the same for IGET, IPUT, SGET, SPUT, AGET and APUT. +enum DexMemAccessType : uint8_t { + kDexMemAccessWord = 0, // op 0; int or float, the actual type is not encoded. + kDexMemAccessWide, // op_WIDE 1; long or double, the actual type is not encoded. + kDexMemAccessObject, // op_OBJECT 2; the actual reference type is not encoded. + kDexMemAccessBoolean, // op_BOOLEAN 3 + kDexMemAccessByte, // op_BYTE 4 + kDexMemAccessChar, // op_CHAR 5 + kDexMemAccessShort, // op_SHORT 6 + kDexMemAccessTypeCount +}; + +std::ostream& operator<<(std::ostream& os, const DexMemAccessType& type); + +// NOTE: The following functions disregard quickened instructions. + +constexpr bool IsInstructionInvoke(Instruction::Code opcode) { + return Instruction::INVOKE_VIRTUAL <= opcode && opcode <= Instruction::INVOKE_INTERFACE_RANGE && + opcode != Instruction::RETURN_VOID_BARRIER; +} + +constexpr bool IsInstructionInvokeStatic(Instruction::Code opcode) { + return opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE; +} + +constexpr bool IsInstructionGoto(Instruction::Code opcode) { + return Instruction::GOTO <= opcode && opcode <= Instruction::GOTO_32; +} + +constexpr bool IsInstructionIfCc(Instruction::Code opcode) { + return Instruction::IF_EQ <= opcode && opcode <= Instruction::IF_LE; +} + +constexpr bool IsInstructionIfCcZ(Instruction::Code opcode) { + return Instruction::IF_EQZ <= opcode && opcode <= Instruction::IF_LEZ; +} + +constexpr bool IsInstructionIGet(Instruction::Code code) { + return Instruction::IGET <= code && code <= Instruction::IGET_SHORT; +} + +constexpr bool IsInstructionIPut(Instruction::Code code) { + return Instruction::IPUT <= code && code <= Instruction::IPUT_SHORT; +} + +constexpr bool IsInstructionSGet(Instruction::Code code) { + return Instruction::SGET <= code && code <= Instruction::SGET_SHORT; +} + +constexpr bool IsInstructionSPut(Instruction::Code code) { + return Instruction::SPUT <= code && code <= Instruction::SPUT_SHORT; +} + +constexpr bool IsInstructionAGet(Instruction::Code code) { + return Instruction::AGET <= code && code <= Instruction::AGET_SHORT; +} + +constexpr bool IsInstructionAPut(Instruction::Code code) { + return Instruction::APUT <= code && code <= Instruction::APUT_SHORT; +} + +constexpr bool IsInstructionIGetOrIPut(Instruction::Code code) { + return Instruction::IGET <= code && code <= Instruction::IPUT_SHORT; +} + +constexpr bool IsInstructionSGetOrSPut(Instruction::Code code) { + return Instruction::SGET <= code && code <= Instruction::SPUT_SHORT; +} + +constexpr bool IsInstructionAGetOrAPut(Instruction::Code code) { + return Instruction::AGET <= code && code <= Instruction::APUT_SHORT; +} + +// TODO: Remove the #if guards below when we fully migrate to C++14. + +constexpr bool IsInvokeInstructionRange(Instruction::Code opcode) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionInvoke(opcode)); +#endif + return opcode >= Instruction::INVOKE_VIRTUAL_RANGE; +} + +constexpr DexInvokeType InvokeInstructionType(Instruction::Code opcode) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionInvoke(opcode)); +#endif + return static_cast<DexInvokeType>(IsInvokeInstructionRange(opcode) + ? (opcode - Instruction::INVOKE_VIRTUAL_RANGE) + : (opcode - Instruction::INVOKE_VIRTUAL)); +} + +constexpr DexMemAccessType IGetMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionIGet(opcode)); +#endif + return static_cast<DexMemAccessType>(code - Instruction::IGET); +} + +constexpr DexMemAccessType IPutMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionIPut(opcode)); +#endif + return static_cast<DexMemAccessType>(code - Instruction::IPUT); +} + +constexpr DexMemAccessType SGetMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionSGet(opcode)); +#endif + return static_cast<DexMemAccessType>(code - Instruction::SGET); +} + +constexpr DexMemAccessType SPutMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionSPut(opcode)); +#endif + return static_cast<DexMemAccessType>(code - Instruction::SPUT); +} + +constexpr DexMemAccessType AGetMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionAGet(opcode)); +#endif + return static_cast<DexMemAccessType>(code - Instruction::AGET); +} + +constexpr DexMemAccessType APutMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionAPut(opcode)); +#endif + return static_cast<DexMemAccessType>(code - Instruction::APUT); +} + +constexpr DexMemAccessType IGetOrIPutMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionIGetOrIPut(opcode)); +#endif + return (code >= Instruction::IPUT) ? IPutMemAccessType(code) : IGetMemAccessType(code); +} + +constexpr DexMemAccessType SGetOrSPutMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionSGetOrSPut(opcode)); +#endif + return (code >= Instruction::SPUT) ? SPutMemAccessType(code) : SGetMemAccessType(code); +} + +constexpr DexMemAccessType AGetOrAPutMemAccessType(Instruction::Code code) { +#if __cplusplus >= 201402 // C++14 allows the DCHECK() in constexpr functions. + DCHECK(IsInstructionAGetOrAPut(opcode)); +#endif + return (code >= Instruction::APUT) ? APutMemAccessType(code) : AGetMemAccessType(code); +} + +} // namespace art + +#endif // ART_COMPILER_UTILS_DEX_INSTRUCTION_UTILS_H_ diff --git a/compiler/utils/scoped_arena_allocator.cc b/compiler/utils/scoped_arena_allocator.cc index 26161501b3..d9e0619de6 100644 --- a/compiler/utils/scoped_arena_allocator.cc +++ b/compiler/utils/scoped_arena_allocator.cc @@ -96,6 +96,7 @@ void* ArenaStack::AllocValgrind(size_t bytes, ArenaAllocKind kind) { uint8_t* ptr = top_ptr_; if (UNLIKELY(static_cast<size_t>(top_end_ - ptr) < rounded_bytes)) { ptr = AllocateFromNextArena(rounded_bytes); + CHECK(ptr != nullptr) << "Failed to allocate memory"; } CurrentStats()->RecordAlloc(bytes, kind); top_ptr_ = ptr + rounded_bytes; diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index afa4a3b958..a297ea3b6e 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -1126,7 +1126,8 @@ void X86Assembler::sarl(Register operand, Register shifter) { } -void X86Assembler::shld(Register dst, Register src) { +void X86Assembler::shld(Register dst, Register src, Register shifter) { + DCHECK_EQ(ECX, shifter); AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); EmitUint8(0xA5); @@ -1134,6 +1135,15 @@ void X86Assembler::shld(Register dst, Register src) { } +void X86Assembler::shrd(Register dst, Register src, Register shifter) { + DCHECK_EQ(ECX, shifter); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xAD); + EmitRegisterOperand(src, dst); +} + + void X86Assembler::negl(Register reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF7); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 8aed9348d6..6ea66a5fa7 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -405,7 +405,8 @@ class X86Assembler FINAL : public Assembler { void shrl(Register operand, Register shifter); void sarl(Register reg, const Immediate& imm); void sarl(Register operand, Register shifter); - void shld(Register dst, Register src); + void shld(Register dst, Register src, Register shifter); + void shrd(Register dst, Register src, Register shifter); void negl(Register reg); void notl(Register reg); diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 8c428f455e..dff3849076 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -1451,8 +1451,18 @@ void X86_64Assembler::shll(CpuRegister reg, const Immediate& imm) { } +void X86_64Assembler::shlq(CpuRegister reg, const Immediate& imm) { + EmitGenericShift(true, 4, reg, imm); +} + + void X86_64Assembler::shll(CpuRegister operand, CpuRegister shifter) { - EmitGenericShift(4, operand, shifter); + EmitGenericShift(false, 4, operand, shifter); +} + + +void X86_64Assembler::shlq(CpuRegister operand, CpuRegister shifter) { + EmitGenericShift(true, 4, operand, shifter); } @@ -1467,7 +1477,12 @@ void X86_64Assembler::shrq(CpuRegister reg, const Immediate& imm) { void X86_64Assembler::shrl(CpuRegister operand, CpuRegister shifter) { - EmitGenericShift(5, operand, shifter); + EmitGenericShift(false, 5, operand, shifter); +} + + +void X86_64Assembler::shrq(CpuRegister operand, CpuRegister shifter) { + EmitGenericShift(true, 5, operand, shifter); } @@ -1477,7 +1492,17 @@ void X86_64Assembler::sarl(CpuRegister reg, const Immediate& imm) { void X86_64Assembler::sarl(CpuRegister operand, CpuRegister shifter) { - EmitGenericShift(7, operand, shifter); + EmitGenericShift(false, 7, operand, shifter); +} + + +void X86_64Assembler::sarq(CpuRegister reg, const Immediate& imm) { + EmitGenericShift(true, 7, reg, imm); +} + + +void X86_64Assembler::sarq(CpuRegister operand, CpuRegister shifter) { + EmitGenericShift(true, 7, operand, shifter); } @@ -1826,12 +1851,17 @@ void X86_64Assembler::EmitGenericShift(bool wide, } -void X86_64Assembler::EmitGenericShift(int reg_or_opcode, +void X86_64Assembler::EmitGenericShift(bool wide, + int reg_or_opcode, CpuRegister operand, CpuRegister shifter) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); CHECK_EQ(shifter.AsRegister(), RCX); - EmitOptionalRex32(operand); + if (wide) { + EmitRex64(operand); + } else { + EmitOptionalRex32(operand); + } EmitUint8(0xD3); EmitOperand(reg_or_opcode, Operand(operand)); } diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 4dd70e27d7..ab1bc9e97d 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -460,7 +460,12 @@ class X86_64Assembler FINAL : public Assembler { void sarl(CpuRegister reg, const Immediate& imm); void sarl(CpuRegister operand, CpuRegister shifter); + void shlq(CpuRegister reg, const Immediate& imm); + void shlq(CpuRegister operand, CpuRegister shifter); void shrq(CpuRegister reg, const Immediate& imm); + void shrq(CpuRegister operand, CpuRegister shifter); + void sarq(CpuRegister reg, const Immediate& imm); + void sarq(CpuRegister operand, CpuRegister shifter); void negl(CpuRegister reg); void negq(CpuRegister reg); @@ -657,7 +662,7 @@ class X86_64Assembler FINAL : public Assembler { void EmitNearLabelLink(Label* label); void EmitGenericShift(bool wide, int rm, CpuRegister reg, const Immediate& imm); - void EmitGenericShift(int rm, CpuRegister operand, CpuRegister shifter); + void EmitGenericShift(bool wide, int rm, CpuRegister operand, CpuRegister shifter); // If any input is not false, output the necessary rex prefix. void EmitOptionalRex(bool force, bool w, bool r, bool x, bool b); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index af389e69ad..14a98b9359 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -296,7 +296,7 @@ TEST_F(AssemblerX86_64Test, SublImm) { DriverStr(Repeatri(&x86_64::X86_64Assembler::subl, 4U, "sub ${imm}, %{reg}"), "subli"); } -// Shll only allows CL as the shift register. +// Shll only allows CL as the shift count. std::string shll_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; @@ -319,7 +319,31 @@ TEST_F(AssemblerX86_64Test, ShllImm) { DriverStr(Repeatri(&x86_64::X86_64Assembler::shll, 1U, "shll ${imm}, %{reg}"), "shlli"); } -// Shrl only allows CL as the shift register. +// Shlq only allows CL as the shift count. +std::string shlq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { + std::ostringstream str; + + std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); + + x86_64::CpuRegister shifter(x86_64::RCX); + for (auto reg : registers) { + assembler->shlq(*reg, shifter); + str << "shlq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; + } + printf("%s\n", str.str().c_str()); + + return str.str(); +} + +TEST_F(AssemblerX86_64Test, ShlqReg) { + DriverFn(&shlq_fn, "shlq"); +} + +TEST_F(AssemblerX86_64Test, ShlqImm) { + DriverStr(RepeatRI(&x86_64::X86_64Assembler::shlq, 1U, "shlq ${imm}, %{reg}"), "shlqi"); +} + +// Shrl only allows CL as the shift count. std::string shrl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; @@ -342,7 +366,30 @@ TEST_F(AssemblerX86_64Test, ShrlImm) { DriverStr(Repeatri(&x86_64::X86_64Assembler::shrl, 1U, "shrl ${imm}, %{reg}"), "shrli"); } -// Sarl only allows CL as the shift register. +// Shrq only allows CL as the shift count. +std::string shrq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { + std::ostringstream str; + + std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); + + x86_64::CpuRegister shifter(x86_64::RCX); + for (auto reg : registers) { + assembler->shrq(*reg, shifter); + str << "shrq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; + } + + return str.str(); +} + +TEST_F(AssemblerX86_64Test, ShrqReg) { + DriverFn(&shrq_fn, "shrq"); +} + +TEST_F(AssemblerX86_64Test, ShrqImm) { + DriverStr(RepeatRI(&x86_64::X86_64Assembler::shrq, 1U, "shrq ${imm}, %{reg}"), "shrqi"); +} + +// Sarl only allows CL as the shift count. std::string sarl_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { std::ostringstream str; @@ -365,6 +412,29 @@ TEST_F(AssemblerX86_64Test, SarlImm) { DriverStr(Repeatri(&x86_64::X86_64Assembler::sarl, 1U, "sarl ${imm}, %{reg}"), "sarli"); } +// Sarq only allows CL as the shift count. +std::string sarq_fn(AssemblerX86_64Test::Base* assembler_test, x86_64::X86_64Assembler* assembler) { + std::ostringstream str; + + std::vector<x86_64::CpuRegister*> registers = assembler_test->GetRegisters(); + + x86_64::CpuRegister shifter(x86_64::RCX); + for (auto reg : registers) { + assembler->sarq(*reg, shifter); + str << "sarq %cl, %" << assembler_test->GetRegisterName(*reg) << "\n"; + } + + return str.str(); +} + +TEST_F(AssemblerX86_64Test, SarqReg) { + DriverFn(&sarq_fn, "sarq"); +} + +TEST_F(AssemblerX86_64Test, SarqImm) { + DriverStr(RepeatRI(&x86_64::X86_64Assembler::sarq, 1U, "sarq ${imm}, %{reg}"), "sarqi"); +} + TEST_F(AssemblerX86_64Test, CmpqRegs) { DriverStr(RepeatRR(&x86_64::X86_64Assembler::cmpq, "cmpq %{reg2}, %{reg1}"), "cmpq"); } |