diff options
Diffstat (limited to 'compiler')
50 files changed, 1469 insertions, 576 deletions
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index eb87c29f9d..02d532798c 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -26,6 +26,30 @@ namespace art { /** + * @class String Change + * @brief Converts calls to String.<init> to StringFactory instead. + */ +class StringChange : public PassME { + public: + StringChange() : PassME("StringChange", kNoNodes) { + } + + void Start(PassDataHolder* data) const { + DCHECK(data != nullptr); + CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit; + DCHECK(c_unit != nullptr); + c_unit->mir_graph->StringChange(); + } + + bool Gate(const PassDataHolder* data) const { + DCHECK(data != nullptr); + CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit; + DCHECK(c_unit != nullptr); + return c_unit->mir_graph->HasInvokes(); + } +}; + +/** * @class CacheFieldLoweringInfo * @brief Cache the lowering info for fields used by IGET/IPUT/SGET/SPUT insns. */ diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc index c538d0beee..c8aa9902c3 100644 --- a/compiler/dex/global_value_numbering_test.cc +++ b/compiler/dex/global_value_numbering_test.cc @@ -290,6 +290,15 @@ class GlobalValueNumberingTest : public testing::Test { DoPrepareVregToSsaMapExit(bb_id, map, count); } + template <size_t count> + void MarkAsWideSRegs(const int32_t (&sregs)[count]) { + for (int32_t sreg : sregs) { + cu_.mir_graph->reg_location_[sreg].wide = true; + cu_.mir_graph->reg_location_[sreg + 1].wide = true; + cu_.mir_graph->reg_location_[sreg + 1].high_word = true; + } + } + void PerformGVN() { DoPerformGVN<LoopRepeatingTopologicalSortIterator>(); } @@ -360,9 +369,11 @@ class GlobalValueNumberingTest : public testing::Test { cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references. Nothing else is used by LVN/GVN. + // 0 constants are integral, not references, and the values are all narrow. + // Nothing else is used by LVN/GVN. Tests can override the default values as needed. cu_.mir_graph->reg_location_ = cu_.arena.AllocArray<RegLocation>(kMaxSsaRegs, kArenaAllocRegAlloc); + cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; // Bind all possible sregs to live vregs for test purposes. live_in_v_->SetInitialBits(kMaxSsaRegs); cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); @@ -910,14 +921,14 @@ TEST_F(GlobalValueNumberingTestDiamond, AliasingArrays) { DEF_IGET(6, Instruction::AGET_OBJECT, 3u, 200u, 201u), // Same as at the left side. DEF_AGET(3, Instruction::AGET_WIDE, 4u, 300u, 301u), - DEF_CONST(5, Instruction::CONST_WIDE, 5u, 1000), - DEF_APUT(5, Instruction::APUT_WIDE, 5u, 300u, 301u), - DEF_AGET(6, Instruction::AGET_WIDE, 7u, 300u, 301u), // Differs from the top and the CONST. + DEF_CONST(5, Instruction::CONST_WIDE, 6u, 1000), + DEF_APUT(5, Instruction::APUT_WIDE, 6u, 300u, 301u), + DEF_AGET(6, Instruction::AGET_WIDE, 8u, 300u, 301u), // Differs from the top and the CONST. - DEF_AGET(3, Instruction::AGET_SHORT, 8u, 400u, 401u), - DEF_CONST(3, Instruction::CONST, 9u, 2000), - DEF_APUT(4, Instruction::APUT_SHORT, 9u, 400u, 401u), - DEF_APUT(5, Instruction::APUT_SHORT, 9u, 400u, 401u), + DEF_AGET(3, Instruction::AGET_SHORT, 10u, 400u, 401u), + DEF_CONST(3, Instruction::CONST, 11u, 2000), + DEF_APUT(4, Instruction::APUT_SHORT, 11u, 400u, 401u), + DEF_APUT(5, Instruction::APUT_SHORT, 11u, 400u, 401u), DEF_AGET(6, Instruction::AGET_SHORT, 12u, 400u, 401u), // Differs from the top, == CONST. DEF_AGET(3, Instruction::AGET_CHAR, 13u, 500u, 501u), @@ -939,6 +950,8 @@ TEST_F(GlobalValueNumberingTestDiamond, AliasingArrays) { }; PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 4, 6, 8 }; + MarkAsWideSRegs(wide_sregs); PerformGVN(); ASSERT_EQ(arraysize(mirs), value_names_.size()); EXPECT_EQ(value_names_[0], value_names_[1]); @@ -1057,6 +1070,12 @@ TEST_F(GlobalValueNumberingTestDiamond, PhiWide) { }; PrepareMIRs(mirs); + for (size_t i = 0u; i != arraysize(mirs); ++i) { + if ((mirs_[i].ssa_rep->defs[0] % 2) == 0) { + const int32_t wide_sregs[] = { mirs_[i].ssa_rep->defs[0] }; + MarkAsWideSRegs(wide_sregs); + } + } PerformGVN(); ASSERT_EQ(arraysize(mirs), value_names_.size()); EXPECT_EQ(value_names_[0], value_names_[7]); @@ -1493,27 +1512,27 @@ TEST_F(GlobalValueNumberingTestLoop, AliasingArrays) { static const MIRDef mirs[] = { // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks. DEF_AGET(3, Instruction::AGET_WIDE, 0u, 100u, 101u), - DEF_AGET(4, Instruction::AGET_WIDE, 1u, 100u, 101u), // Same as at the top. - DEF_AGET(5, Instruction::AGET_WIDE, 2u, 100u, 101u), // Same as at the top. + DEF_AGET(4, Instruction::AGET_WIDE, 2u, 100u, 101u), // Same as at the top. + DEF_AGET(5, Instruction::AGET_WIDE, 4u, 100u, 101u), // Same as at the top. - DEF_AGET(3, Instruction::AGET_BYTE, 3u, 200u, 201u), - DEF_AGET(4, Instruction::AGET_BYTE, 4u, 200u, 201u), // Differs from top... - DEF_APUT(4, Instruction::APUT_BYTE, 5u, 200u, 201u), // Because of this IPUT. - DEF_AGET(5, Instruction::AGET_BYTE, 6u, 200u, 201u), // Differs from top and the loop AGET. + DEF_AGET(3, Instruction::AGET_BYTE, 6u, 200u, 201u), + DEF_AGET(4, Instruction::AGET_BYTE, 7u, 200u, 201u), // Differs from top... + DEF_APUT(4, Instruction::APUT_BYTE, 8u, 200u, 201u), // Because of this IPUT. + DEF_AGET(5, Instruction::AGET_BYTE, 9u, 200u, 201u), // Differs from top and the loop AGET. - DEF_AGET(3, Instruction::AGET, 7u, 300u, 301u), - DEF_APUT(4, Instruction::APUT, 8u, 300u, 301u), // Because of this IPUT... - DEF_AGET(4, Instruction::AGET, 9u, 300u, 301u), // Differs from top. - DEF_AGET(5, Instruction::AGET, 10u, 300u, 301u), // Differs from top but == the loop AGET. + DEF_AGET(3, Instruction::AGET, 10u, 300u, 301u), + DEF_APUT(4, Instruction::APUT, 11u, 300u, 301u), // Because of this IPUT... + DEF_AGET(4, Instruction::AGET, 12u, 300u, 301u), // Differs from top. + DEF_AGET(5, Instruction::AGET, 13u, 300u, 301u), // Differs from top but == the loop AGET. - DEF_CONST(3, Instruction::CONST, 11u, 3000), - DEF_APUT(3, Instruction::APUT_CHAR, 11u, 400u, 401u), - DEF_APUT(3, Instruction::APUT_CHAR, 11u, 400u, 402u), - DEF_AGET(4, Instruction::AGET_CHAR, 14u, 400u, 401u), // Differs from 11u and 16u. - DEF_AGET(4, Instruction::AGET_CHAR, 15u, 400u, 402u), // Same as 14u. - DEF_CONST(4, Instruction::CONST, 16u, 4000), - DEF_APUT(4, Instruction::APUT_CHAR, 16u, 400u, 401u), - DEF_APUT(4, Instruction::APUT_CHAR, 16u, 400u, 402u), + DEF_CONST(3, Instruction::CONST, 14u, 3000), + DEF_APUT(3, Instruction::APUT_CHAR, 14u, 400u, 401u), + DEF_APUT(3, Instruction::APUT_CHAR, 14u, 400u, 402u), + DEF_AGET(4, Instruction::AGET_CHAR, 15u, 400u, 401u), // Differs from 11u and 16u. + DEF_AGET(4, Instruction::AGET_CHAR, 16u, 400u, 402u), // Same as 14u. + DEF_CONST(4, Instruction::CONST, 17u, 4000), + DEF_APUT(4, Instruction::APUT_CHAR, 17u, 400u, 401u), + DEF_APUT(4, Instruction::APUT_CHAR, 17u, 400u, 402u), DEF_AGET(5, Instruction::AGET_CHAR, 19u, 400u, 401u), // Differs from 11u and 14u... DEF_AGET(5, Instruction::AGET_CHAR, 20u, 400u, 402u), // and same as the CONST 16u. @@ -1531,6 +1550,8 @@ TEST_F(GlobalValueNumberingTestLoop, AliasingArrays) { }; PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 0, 2, 4 }; + MarkAsWideSRegs(wide_sregs); PerformGVN(); ASSERT_EQ(arraysize(mirs), value_names_.size()); EXPECT_EQ(value_names_[0], value_names_[1]); diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc index bd7bd71e73..4f0e9d1b67 100644 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ b/compiler/dex/gvn_dead_code_elimination.cc @@ -984,18 +984,17 @@ bool GvnDeadCodeElimination::RecordMIR(MIR* mir) { uint16_t opcode = mir->dalvikInsn.opcode; switch (opcode) { case kMirOpPhi: { - // We can't recognize wide variables in Phi from num_defs == 2 as we've got two Phis instead. + // Determine if this Phi is merging wide regs. + RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); + if (raw_dest.high_word) { + // This is the high part of a wide reg. Ignore the Phi. + return false; + } + bool wide = raw_dest.wide; + // Record the value. DCHECK_EQ(mir->ssa_rep->num_defs, 1); int s_reg = mir->ssa_rep->defs[0]; - bool wide = false; - uint16_t new_value = lvn_->GetSregValue(s_reg); - if (new_value == kNoValue) { - wide = true; - new_value = lvn_->GetSregValueWide(s_reg); - if (new_value == kNoValue) { - return false; // Ignore the high word Phi. - } - } + uint16_t new_value = wide ? lvn_->GetSregValueWide(s_reg) : lvn_->GetSregValue(s_reg); int v_reg = mir_graph_->SRegToVReg(s_reg); DCHECK_EQ(vreg_chains_.CurrentValue(v_reg), kNoValue); // No previous def for v_reg. diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index 3eb372ce05..f9f0882f08 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -406,6 +406,15 @@ class GvnDeadCodeEliminationTest : public testing::Test { } } + template <size_t count> + void MarkAsWideSRegs(const int32_t (&sregs)[count]) { + for (int32_t sreg : sregs) { + cu_.mir_graph->reg_location_[sreg].wide = true; + cu_.mir_graph->reg_location_[sreg + 1].wide = true; + cu_.mir_graph->reg_location_[sreg + 1].high_word = true; + } + } + void PerformDCE() { FillVregToSsaRegExitMaps(); cu_.mir_graph->GetNumOfCodeAndTempVRs(); @@ -467,9 +476,11 @@ class GvnDeadCodeEliminationTest : public testing::Test { cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test. allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references. Nothing else is used by LVN/GVN. + // 0 constants are integral, not references, and the values are all narrow. + // Nothing else is used by LVN/GVN. Tests can override the default values as needed. cu_.mir_graph->reg_location_ = static_cast<RegLocation*>(cu_.arena.Alloc( kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); + cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; // Bind all possible sregs to live vregs for test purposes. live_in_v_->SetInitialBits(kMaxSsaRegs); cu_.mir_graph->ssa_base_vregs_.reserve(kMaxSsaRegs); @@ -705,6 +716,8 @@ TEST_F(GvnDeadCodeEliminationTestSimple, Rename4) { PrepareSRegToVRegMap(sreg_to_vreg_map); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 3 }; + MarkAsWideSRegs(wide_sregs); PerformGVN_DCE(); ASSERT_EQ(arraysize(mirs), value_names_.size()); @@ -745,6 +758,8 @@ TEST_F(GvnDeadCodeEliminationTestSimple, Rename5) { PrepareIFields(ifields); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 5 }; + MarkAsWideSRegs(wide_sregs); PerformGVN_DCE(); ASSERT_EQ(arraysize(mirs), value_names_.size()); @@ -777,6 +792,8 @@ TEST_F(GvnDeadCodeEliminationTestSimple, Rename6) { PrepareSRegToVRegMap(sreg_to_vreg_map); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 0, 2 }; + MarkAsWideSRegs(wide_sregs); PerformGVN_DCE(); ASSERT_EQ(arraysize(mirs), value_names_.size()); @@ -1255,6 +1272,8 @@ TEST_F(GvnDeadCodeEliminationTestSimple, Simple4) { PrepareIFields(ifields); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 1, 6 }; + MarkAsWideSRegs(wide_sregs); PerformGVN_DCE(); ASSERT_EQ(arraysize(mirs), value_names_.size()); diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc index cdf5e38a9c..cc9dbe4adb 100644 --- a/compiler/dex/local_value_numbering.cc +++ b/compiler/dex/local_value_numbering.cc @@ -1152,28 +1152,20 @@ uint16_t LocalValueNumbering::HandlePhi(MIR* mir) { // Running LVN without a full GVN? return kNoValue; } - int32_t* uses = mir->ssa_rep->uses; - // Try to find out if this is merging wide regs. - if (mir->ssa_rep->defs[0] != 0 && - sreg_wide_value_map_.count(mir->ssa_rep->defs[0] - 1) != 0u) { + // Determine if this Phi is merging wide regs. + RegLocation raw_dest = gvn_->GetMirGraph()->GetRawDest(mir); + if (raw_dest.high_word) { // This is the high part of a wide reg. Ignore the Phi. return kNoValue; } - BasicBlockId* incoming = mir->meta.phi_incoming; - int16_t pos = 0; - // Check if we're merging a wide value based on the first merged LVN. - const LocalValueNumbering* first_lvn = gvn_->merge_lvns_[0]; - DCHECK_LT(pos, mir->ssa_rep->num_uses); - while (incoming[pos] != first_lvn->Id()) { - ++pos; - DCHECK_LT(pos, mir->ssa_rep->num_uses); - } - int first_s_reg = uses[pos]; - bool wide = (first_lvn->sreg_wide_value_map_.count(first_s_reg) != 0u); + bool wide = raw_dest.wide; // Iterate over *merge_lvns_ and skip incoming sregs for BBs without associated LVN. merge_names_.clear(); uint16_t value_name = kNoValue; bool same_values = true; + BasicBlockId* incoming = mir->meta.phi_incoming; + int32_t* uses = mir->ssa_rep->uses; + int16_t pos = 0; for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) { DCHECK_LT(pos, mir->ssa_rep->num_uses); while (incoming[pos] != lvn->Id()) { @@ -1994,6 +1986,9 @@ uint16_t LocalValueNumbering::GetEndingVregValueNumberImpl(int v_reg, bool wide) if (s_reg == INVALID_SREG) { return kNoValue; } + if (gvn_->GetMirGraph()->GetRegLocation(s_reg).wide != wide) { + return kNoValue; + } if (wide) { int high_s_reg = bb->data_flow_info->vreg_to_ssa_map_exit[v_reg + 1]; if (high_s_reg != s_reg + 1) { diff --git a/compiler/dex/local_value_numbering.h b/compiler/dex/local_value_numbering.h index 379c952fe8..67fb647eba 100644 --- a/compiler/dex/local_value_numbering.h +++ b/compiler/dex/local_value_numbering.h @@ -53,10 +53,12 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { } uint16_t GetSregValue(uint16_t s_reg) const { + DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); return GetSregValueImpl(s_reg, &sreg_value_map_); } uint16_t GetSregValueWide(uint16_t s_reg) const { + DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); return GetSregValueImpl(s_reg, &sreg_wide_value_map_); } @@ -123,21 +125,27 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { void SetOperandValue(uint16_t s_reg, uint16_t value) { DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u); + DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); SetOperandValueImpl(s_reg, value, &sreg_value_map_); } uint16_t GetOperandValue(int s_reg) const { DCHECK_EQ(sreg_wide_value_map_.count(s_reg), 0u); + DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); return GetOperandValueImpl(s_reg, &sreg_value_map_); } void SetOperandValueWide(uint16_t s_reg, uint16_t value) { DCHECK_EQ(sreg_value_map_.count(s_reg), 0u); + DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); + DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).high_word); SetOperandValueImpl(s_reg, value, &sreg_wide_value_map_); } uint16_t GetOperandValueWide(int s_reg) const { DCHECK_EQ(sreg_value_map_.count(s_reg), 0u); + DCHECK(gvn_->GetMirGraph()->GetRegLocation(s_reg).wide); + DCHECK(!gvn_->GetMirGraph()->GetRegLocation(s_reg).high_word); return GetOperandValueImpl(s_reg, &sreg_wide_value_map_); } @@ -331,7 +339,7 @@ class LocalValueNumbering : public DeletableArenaObject<kArenaAllocMisc> { void CopyLiveSregValues(SregValueMap* dest, const SregValueMap& src); - // Intersect maps as sets. The value type must be equality-comparable. + // Intersect SSA reg value maps as sets, ignore dead regs. template <SregValueMap LocalValueNumbering::* map_ptr> void IntersectSregValueMaps(); diff --git a/compiler/dex/local_value_numbering_test.cc b/compiler/dex/local_value_numbering_test.cc index 0393410867..bd00690270 100644 --- a/compiler/dex/local_value_numbering_test.cc +++ b/compiler/dex/local_value_numbering_test.cc @@ -182,6 +182,15 @@ class LocalValueNumberingTest : public testing::Test { ~MirSFieldLoweringInfo::kFlagClassIsInitialized; } + template <size_t count> + void MarkAsWideSRegs(const int32_t (&sregs)[count]) { + for (int32_t sreg : sregs) { + cu_.mir_graph->reg_location_[sreg].wide = true; + cu_.mir_graph->reg_location_[sreg + 1].wide = true; + cu_.mir_graph->reg_location_[sreg + 1].high_word = true; + } + } + void PerformLVN() { cu_.mir_graph->temp_.gvn.ifield_ids = GlobalValueNumbering::PrepareGvnFieldIds( allocator_.get(), cu_.mir_graph->ifield_lowering_infos_); @@ -210,9 +219,11 @@ class LocalValueNumberingTest : public testing::Test { cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack)); // By default, the zero-initialized reg_location_[.] with ref == false tells LVN that - // 0 constants are integral, not references. Nothing else is used by LVN/GVN. + // 0 constants are integral, not references, and the values are all narrow. + // Nothing else is used by LVN/GVN. Tests can override the default values as needed. cu_.mir_graph->reg_location_ = static_cast<RegLocation*>(cu_.arena.Alloc( kMaxSsaRegs * sizeof(cu_.mir_graph->reg_location_[0]), kArenaAllocRegAlloc)); + cu_.mir_graph->num_ssa_regs_ = kMaxSsaRegs; } static constexpr size_t kMaxSsaRegs = 16384u; @@ -379,26 +390,28 @@ TEST_F(LocalValueNumberingTest, UnresolvedIField) { { 3u, 0u, 0u, false, kDexMemAccessWord }, // Unresolved field. }; static const MIRDef mirs[] = { - DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 20u), - DEF_IGET(Instruction::IGET, 1u, 20u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 2u, 21u, 0u), // Resolved field #1. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 3u, 21u, 1u), // Resolved field #2. - DEF_IGET(Instruction::IGET, 4u, 22u, 2u), // Unresolved IGET can be "acquire". - DEF_IGET(Instruction::IGET, 5u, 20u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 6u, 21u, 0u), // Resolved field #1. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 7u, 21u, 1u), // Resolved field #2. - DEF_IPUT(Instruction::IPUT, 8u, 22u, 2u), // IPUT clobbers field #1 (#2 is wide). - DEF_IGET(Instruction::IGET, 9u, 20u, 0u), // Resolved field #1, unique object. - DEF_IGET(Instruction::IGET, 10u, 21u, 0u), // Resolved field #1, new value name. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 11u, 21u, 1u), // Resolved field #2. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 12u, 20u, 1u), // Resolved field #2, unique object. - DEF_IPUT(Instruction::IPUT, 13u, 20u, 2u), // IPUT clobbers field #1 (#2 is wide). - DEF_IGET(Instruction::IGET, 14u, 20u, 0u), // Resolved field #1, unique object. - DEF_IGET_WIDE(Instruction::IGET_WIDE, 15u, 20u, 1u), // Resolved field #2, unique object. + DEF_UNIQUE_REF(Instruction::NEW_INSTANCE, 30u), + DEF_IGET(Instruction::IGET, 1u, 30u, 0u), // Resolved field #1, unique object. + DEF_IGET(Instruction::IGET, 2u, 31u, 0u), // Resolved field #1. + DEF_IGET_WIDE(Instruction::IGET_WIDE, 3u, 31u, 1u), // Resolved field #2. + DEF_IGET(Instruction::IGET, 5u, 32u, 2u), // Unresolved IGET can be "acquire". + DEF_IGET(Instruction::IGET, 6u, 30u, 0u), // Resolved field #1, unique object. + DEF_IGET(Instruction::IGET, 7u, 31u, 0u), // Resolved field #1. + DEF_IGET_WIDE(Instruction::IGET_WIDE, 8u, 31u, 1u), // Resolved field #2. + DEF_IPUT(Instruction::IPUT, 10u, 32u, 2u), // IPUT clobbers field #1 (#2 is wide). + DEF_IGET(Instruction::IGET, 11u, 30u, 0u), // Resolved field #1, unique object. + DEF_IGET(Instruction::IGET, 12u, 31u, 0u), // Resolved field #1, new value name. + DEF_IGET_WIDE(Instruction::IGET_WIDE, 13u, 31u, 1u), // Resolved field #2. + DEF_IGET_WIDE(Instruction::IGET_WIDE, 15u, 30u, 1u), // Resolved field #2, unique object. + DEF_IPUT(Instruction::IPUT, 17u, 30u, 2u), // IPUT clobbers field #1 (#2 is wide). + DEF_IGET(Instruction::IGET, 18u, 30u, 0u), // Resolved field #1, unique object. + DEF_IGET_WIDE(Instruction::IGET_WIDE, 19u, 30u, 1u), // Resolved field #2, unique object. }; PrepareIFields(ifields); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 3, 8, 13, 15, 19 }; + MarkAsWideSRegs(wide_sregs); PerformLVN(); ASSERT_EQ(value_names_.size(), 16u); // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics. @@ -430,16 +443,18 @@ TEST_F(LocalValueNumberingTest, UnresolvedSField) { static const MIRDef mirs[] = { DEF_SGET(Instruction::SGET, 0u, 0u), // Resolved field #1. DEF_SGET_WIDE(Instruction::SGET_WIDE, 1u, 1u), // Resolved field #2. - DEF_SGET(Instruction::SGET, 2u, 2u), // Unresolved SGET can be "acquire". - DEF_SGET(Instruction::SGET, 3u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 4u, 1u), // Resolved field #2. - DEF_SPUT(Instruction::SPUT, 5u, 2u), // SPUT clobbers field #1 (#2 is wide). - DEF_SGET(Instruction::SGET, 6u, 0u), // Resolved field #1. - DEF_SGET_WIDE(Instruction::SGET_WIDE, 7u, 1u), // Resolved field #2. + DEF_SGET(Instruction::SGET, 3u, 2u), // Unresolved SGET can be "acquire". + DEF_SGET(Instruction::SGET, 4u, 0u), // Resolved field #1. + DEF_SGET_WIDE(Instruction::SGET_WIDE, 5u, 1u), // Resolved field #2. + DEF_SPUT(Instruction::SPUT, 7u, 2u), // SPUT clobbers field #1 (#2 is wide). + DEF_SGET(Instruction::SGET, 8u, 0u), // Resolved field #1. + DEF_SGET_WIDE(Instruction::SGET_WIDE, 9u, 1u), // Resolved field #2. }; PrepareSFields(sfields); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 1, 5, 9 }; + MarkAsWideSRegs(wide_sregs); PerformLVN(); ASSERT_EQ(value_names_.size(), 8u); // Unresolved field is potentially volatile, so we need to adhere to the volatile semantics. @@ -585,18 +600,20 @@ TEST_F(LocalValueNumberingTest, EscapingRefs) { DEF_IGET(Instruction::IGET, 7u, 20u, 0u), // New value. DEF_IGET(Instruction::IGET, 8u, 20u, 1u), // Still the same. DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 9u, 31u, 3u), // No aliasing, different type. - DEF_IGET(Instruction::IGET, 10u, 20u, 0u), - DEF_IGET(Instruction::IGET, 11u, 20u, 1u), - DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 12u, 31u, 5u), // No aliasing, different type. - DEF_IGET(Instruction::IGET, 13u, 20u, 0u), - DEF_IGET(Instruction::IGET, 14u, 20u, 1u), - DEF_IPUT(Instruction::IPUT, 15u, 31u, 4u), // Aliasing, same type. - DEF_IGET(Instruction::IGET, 16u, 20u, 0u), - DEF_IGET(Instruction::IGET, 17u, 20u, 1u), + DEF_IGET(Instruction::IGET, 11u, 20u, 0u), + DEF_IGET(Instruction::IGET, 12u, 20u, 1u), + DEF_IPUT_WIDE(Instruction::IPUT_WIDE, 13u, 31u, 5u), // No aliasing, different type. + DEF_IGET(Instruction::IGET, 15u, 20u, 0u), + DEF_IGET(Instruction::IGET, 16u, 20u, 1u), + DEF_IPUT(Instruction::IPUT, 17u, 31u, 4u), // Aliasing, same type. + DEF_IGET(Instruction::IGET, 18u, 20u, 0u), + DEF_IGET(Instruction::IGET, 19u, 20u, 1u), }; PrepareIFields(ifields); PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 9, 13 }; + MarkAsWideSRegs(wide_sregs); PerformLVN(); ASSERT_EQ(value_names_.size(), 18u); EXPECT_EQ(value_names_[1], value_names_[4]); @@ -626,14 +643,16 @@ TEST_F(LocalValueNumberingTest, EscapingArrayRefs) { DEF_AGET(Instruction::AGET, 4u, 20u, 40u), DEF_AGET(Instruction::AGET, 5u, 20u, 41u), DEF_APUT_WIDE(Instruction::APUT_WIDE, 6u, 31u, 43u), // No aliasing, different type. - DEF_AGET(Instruction::AGET, 7u, 20u, 40u), - DEF_AGET(Instruction::AGET, 8u, 20u, 41u), - DEF_APUT(Instruction::APUT, 9u, 32u, 40u), // May alias with all elements. - DEF_AGET(Instruction::AGET, 10u, 20u, 40u), // New value (same index name). - DEF_AGET(Instruction::AGET, 11u, 20u, 41u), // New value (different index name). + DEF_AGET(Instruction::AGET, 8u, 20u, 40u), + DEF_AGET(Instruction::AGET, 9u, 20u, 41u), + DEF_APUT(Instruction::APUT, 10u, 32u, 40u), // May alias with all elements. + DEF_AGET(Instruction::AGET, 11u, 20u, 40u), // New value (same index name). + DEF_AGET(Instruction::AGET, 12u, 20u, 41u), // New value (different index name). }; PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 6 }; + MarkAsWideSRegs(wide_sregs); PerformLVN(); ASSERT_EQ(value_names_.size(), 12u); EXPECT_EQ(value_names_[1], value_names_[4]); @@ -769,6 +788,8 @@ TEST_F(LocalValueNumberingTest, DivZeroCheck) { }; PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 5, 7, 12, 14, 16 }; + MarkAsWideSRegs(wide_sregs); PerformLVN(); for (size_t i = 0u; i != mir_count_; ++i) { int expected = expected_ignore_div_zero_check[i] ? MIR_IGNORE_DIV_ZERO_CHECK : 0u; @@ -780,51 +801,55 @@ TEST_F(LocalValueNumberingTest, ConstWide) { static const MIRDef mirs[] = { // Core reg constants. DEF_CONST(Instruction::CONST_WIDE_16, 0u, 0), - DEF_CONST(Instruction::CONST_WIDE_16, 1u, 1), - DEF_CONST(Instruction::CONST_WIDE_16, 2u, -1), - DEF_CONST(Instruction::CONST_WIDE_32, 3u, 1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 4u, -1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 5u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 6u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE_32, 7u, -(1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 8u, -(1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE, 9u, INT64_C(1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 10u, INT64_C(-1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 11u, (INT64_C(1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 12u, (INT64_C(1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE, 13u, (INT64_C(-1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 14u, (INT64_C(-1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 15u, 1), // Effectively 1 << 48. - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 16u, 0xffff), // Effectively -1 << 48. - DEF_CONST(Instruction::CONST_WIDE, 17u, (INT64_C(1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 18u, (INT64_C(1) << 48) - 1), - DEF_CONST(Instruction::CONST_WIDE, 19u, (INT64_C(-1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 20u, (INT64_C(-1) << 48) - 1), + DEF_CONST(Instruction::CONST_WIDE_16, 2u, 1), + DEF_CONST(Instruction::CONST_WIDE_16, 4u, -1), + DEF_CONST(Instruction::CONST_WIDE_32, 6u, 1 << 16), + DEF_CONST(Instruction::CONST_WIDE_32, 8u, -1 << 16), + DEF_CONST(Instruction::CONST_WIDE_32, 10u, (1 << 16) + 1), + DEF_CONST(Instruction::CONST_WIDE_32, 12u, (1 << 16) - 1), + DEF_CONST(Instruction::CONST_WIDE_32, 14u, -(1 << 16) + 1), + DEF_CONST(Instruction::CONST_WIDE_32, 16u, -(1 << 16) - 1), + DEF_CONST(Instruction::CONST_WIDE, 18u, INT64_C(1) << 32), + DEF_CONST(Instruction::CONST_WIDE, 20u, INT64_C(-1) << 32), + DEF_CONST(Instruction::CONST_WIDE, 22u, (INT64_C(1) << 32) + 1), + DEF_CONST(Instruction::CONST_WIDE, 24u, (INT64_C(1) << 32) - 1), + DEF_CONST(Instruction::CONST_WIDE, 26u, (INT64_C(-1) << 32) + 1), + DEF_CONST(Instruction::CONST_WIDE, 28u, (INT64_C(-1) << 32) - 1), + DEF_CONST(Instruction::CONST_WIDE_HIGH16, 30u, 1), // Effectively 1 << 48. + DEF_CONST(Instruction::CONST_WIDE_HIGH16, 32u, 0xffff), // Effectively -1 << 48. + DEF_CONST(Instruction::CONST_WIDE, 34u, (INT64_C(1) << 48) + 1), + DEF_CONST(Instruction::CONST_WIDE, 36u, (INT64_C(1) << 48) - 1), + DEF_CONST(Instruction::CONST_WIDE, 38u, (INT64_C(-1) << 48) + 1), + DEF_CONST(Instruction::CONST_WIDE, 40u, (INT64_C(-1) << 48) - 1), // FP reg constants. - DEF_CONST(Instruction::CONST_WIDE_16, 21u, 0), - DEF_CONST(Instruction::CONST_WIDE_16, 22u, 1), - DEF_CONST(Instruction::CONST_WIDE_16, 23u, -1), - DEF_CONST(Instruction::CONST_WIDE_32, 24u, 1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 25u, -1 << 16), - DEF_CONST(Instruction::CONST_WIDE_32, 26u, (1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 27u, (1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE_32, 28u, -(1 << 16) + 1), - DEF_CONST(Instruction::CONST_WIDE_32, 29u, -(1 << 16) - 1), - DEF_CONST(Instruction::CONST_WIDE, 30u, INT64_C(1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 31u, INT64_C(-1) << 32), - DEF_CONST(Instruction::CONST_WIDE, 32u, (INT64_C(1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 33u, (INT64_C(1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE, 34u, (INT64_C(-1) << 32) + 1), - DEF_CONST(Instruction::CONST_WIDE, 35u, (INT64_C(-1) << 32) - 1), - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 36u, 1), // Effectively 1 << 48. - DEF_CONST(Instruction::CONST_WIDE_HIGH16, 37u, 0xffff), // Effectively -1 << 48. - DEF_CONST(Instruction::CONST_WIDE, 38u, (INT64_C(1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 39u, (INT64_C(1) << 48) - 1), - DEF_CONST(Instruction::CONST_WIDE, 40u, (INT64_C(-1) << 48) + 1), - DEF_CONST(Instruction::CONST_WIDE, 41u, (INT64_C(-1) << 48) - 1), + DEF_CONST(Instruction::CONST_WIDE_16, 42u, 0), + DEF_CONST(Instruction::CONST_WIDE_16, 44u, 1), + DEF_CONST(Instruction::CONST_WIDE_16, 46u, -1), + DEF_CONST(Instruction::CONST_WIDE_32, 48u, 1 << 16), + DEF_CONST(Instruction::CONST_WIDE_32, 50u, -1 << 16), + DEF_CONST(Instruction::CONST_WIDE_32, 52u, (1 << 16) + 1), + DEF_CONST(Instruction::CONST_WIDE_32, 54u, (1 << 16) - 1), + DEF_CONST(Instruction::CONST_WIDE_32, 56u, -(1 << 16) + 1), + DEF_CONST(Instruction::CONST_WIDE_32, 58u, -(1 << 16) - 1), + DEF_CONST(Instruction::CONST_WIDE, 60u, INT64_C(1) << 32), + DEF_CONST(Instruction::CONST_WIDE, 62u, INT64_C(-1) << 32), + DEF_CONST(Instruction::CONST_WIDE, 64u, (INT64_C(1) << 32) + 1), + DEF_CONST(Instruction::CONST_WIDE, 66u, (INT64_C(1) << 32) - 1), + DEF_CONST(Instruction::CONST_WIDE, 68u, (INT64_C(-1) << 32) + 1), + DEF_CONST(Instruction::CONST_WIDE, 70u, (INT64_C(-1) << 32) - 1), + DEF_CONST(Instruction::CONST_WIDE_HIGH16, 72u, 1), // Effectively 1 << 48. + DEF_CONST(Instruction::CONST_WIDE_HIGH16, 74u, 0xffff), // Effectively -1 << 48. + DEF_CONST(Instruction::CONST_WIDE, 76u, (INT64_C(1) << 48) + 1), + DEF_CONST(Instruction::CONST_WIDE, 78u, (INT64_C(1) << 48) - 1), + DEF_CONST(Instruction::CONST_WIDE, 80u, (INT64_C(-1) << 48) + 1), + DEF_CONST(Instruction::CONST_WIDE, 82u, (INT64_C(-1) << 48) - 1), }; PrepareMIRs(mirs); + for (size_t i = 0; i != arraysize(mirs); ++i) { + const int32_t wide_sregs[] = { mirs_[i].ssa_rep->defs[0] }; + MarkAsWideSRegs(wide_sregs); + } for (size_t i = arraysize(mirs) / 2u; i != arraysize(mirs); ++i) { cu_.mir_graph->reg_location_[mirs_[i].ssa_rep->defs[0]].fp = true; } diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 7bfbb34ae9..7385a8bd53 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -519,6 +519,7 @@ struct CallInfo { bool is_range; DexOffset offset; // Offset in code units. MIR* mir; + int32_t string_init_offset; }; @@ -723,6 +724,8 @@ class MIRGraph { void BasicBlockOptimization(); void BasicBlockOptimizationEnd(); + void StringChange(); + const ArenaVector<BasicBlockId>& GetTopologicalSortOrder() { DCHECK(!topological_order_.empty()); return topological_order_; diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc index 0c84b82edd..5654604797 100644 --- a/compiler/dex/mir_method_info.cc +++ b/compiler/dex/mir_method_info.cc @@ -16,6 +16,7 @@ # include "mir_method_info.h" +#include "dex/compiler_ir.h" #include "dex/quick/dex_file_method_inliner.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/verified_method.h" @@ -83,6 +84,13 @@ void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver, MethodReference* devirt_target = (it->target_dex_file_ != nullptr) ? &devirt_ref : nullptr; InvokeType invoke_type = it->GetInvokeType(); mirror::ArtMethod* resolved_method = nullptr; + + bool string_init = false; + if (default_inliner->IsStringInitMethodIndex(it->MethodIndex())) { + string_init = true; + invoke_type = kDirect; + } + if (!it->IsQuickened()) { it->target_dex_file_ = dex_file; it->target_method_idx_ = it->MethodIndex(); @@ -170,6 +178,9 @@ void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver, it->target_dex_file_ = target_method.dex_file; it->target_method_idx_ = target_method.dex_method_index; it->stats_flags_ = fast_path_flags; + if (string_init) { + it->direct_code_ = 0; + } } } diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc index f7107c159d..217dbeeb44 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -18,6 +18,7 @@ #include "base/logging.h" #include "base/scoped_arena_containers.h" #include "dataflow_iterator-inl.h" +#include "dex/verified_method.h" #include "dex_flags.h" #include "driver/compiler_driver.h" #include "driver/dex_compilation_unit.h" @@ -25,10 +26,11 @@ #include "gvn_dead_code_elimination.h" #include "local_value_numbering.h" #include "mir_field_info.h" -#include "type_inference.h" +#include "mirror/string.h" #include "quick/dex_file_method_inliner.h" #include "quick/dex_file_to_method_inliner_map.h" #include "stack.h" +#include "type_inference.h" namespace art { @@ -1660,6 +1662,77 @@ void MIRGraph::BasicBlockOptimizationEnd() { temp_scoped_alloc_.reset(); } +void MIRGraph::StringChange() { + AllNodesIterator iter(this); + for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) { + for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) { + // Look for new instance opcodes, skip otherwise + Instruction::Code opcode = mir->dalvikInsn.opcode; + if (opcode == Instruction::NEW_INSTANCE) { + uint32_t type_idx = mir->dalvikInsn.vB; + if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) { + // Change NEW_INSTANCE and throwing half of the insn (if it exists) into CONST_4 of 0 + mir->dalvikInsn.opcode = Instruction::CONST_4; + mir->dalvikInsn.vB = 0; + MIR* check_mir = GetBasicBlock(bb->predecessors[0])->last_mir_insn; + if (check_mir != nullptr && + static_cast<int>(check_mir->dalvikInsn.opcode) == kMirOpCheck) { + check_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop); + check_mir->dalvikInsn.vB = 0; + } + } + } else if ((opcode == Instruction::INVOKE_DIRECT) || + (opcode == Instruction::INVOKE_DIRECT_RANGE)) { + uint32_t method_idx = mir->dalvikInsn.vB; + DexFileMethodInliner* inliner = + cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file); + if (inliner->IsStringInitMethodIndex(method_idx)) { + bool is_range = (opcode == Instruction::INVOKE_DIRECT_RANGE); + uint32_t orig_this_reg = is_range ? mir->dalvikInsn.vC : mir->dalvikInsn.arg[0]; + // Remove this pointer from string init and change to static call. + mir->dalvikInsn.vA--; + if (!is_range) { + mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC; + for (uint32_t i = 0; i < mir->dalvikInsn.vA; i++) { + mir->dalvikInsn.arg[i] = mir->dalvikInsn.arg[i + 1]; + } + } else { + mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC_RANGE; + mir->dalvikInsn.vC++; + } + // Insert a move-result instruction to the original this pointer reg. + MIR* move_result_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR)); + move_result_mir->dalvikInsn.opcode = Instruction::MOVE_RESULT_OBJECT; + move_result_mir->dalvikInsn.vA = orig_this_reg; + move_result_mir->offset = mir->offset; + move_result_mir->m_unit_index = mir->m_unit_index; + bb->InsertMIRAfter(mir, move_result_mir); + // Add additional moves if this pointer was copied to other registers. + const VerifiedMethod* verified_method = + cu_->compiler_driver->GetVerifiedMethod(cu_->dex_file, cu_->method_idx); + DCHECK(verified_method != nullptr); + const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map = + verified_method->GetStringInitPcRegMap(); + auto map_it = string_init_map.find(mir->offset); + if (map_it != string_init_map.end()) { + const std::set<uint32_t>& reg_set = map_it->second; + for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { + MIR* move_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR)); + move_mir->dalvikInsn.opcode = Instruction::MOVE_OBJECT; + move_mir->dalvikInsn.vA = *set_it; + move_mir->dalvikInsn.vB = orig_this_reg; + move_mir->offset = mir->offset; + move_mir->m_unit_index = mir->m_unit_index; + bb->InsertMIRAfter(move_result_mir, move_mir); + } + } + } + } + } + } +} + + bool MIRGraph::EliminateSuspendChecksGate() { if ((cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled. GetMaxNestedLoops() == 0u || // Nothing to do. diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc index 3e193b471b..375003bf1f 100644 --- a/compiler/dex/pass_driver_me_opts.cc +++ b/compiler/dex/pass_driver_me_opts.cc @@ -35,6 +35,7 @@ void PassDriverMEOpts::SetupPasses(PassManager* pass_manager) { * Disadvantage is the passes can't change their internal states depending on CompilationUnit: * - This is not yet an issue: no current pass would require it. */ + pass_manager->AddPass(new StringChange); pass_manager->AddPass(new CacheFieldLoweringInfo); pass_manager->AddPass(new CacheMethodLoweringInfo); pass_manager->AddPass(new CalculatePredecessors); diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 6ba4016260..2b2d6af326 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -21,6 +21,7 @@ #include "arm_lir.h" #include "base/logging.h" #include "dex/mir_graph.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/quick/mir_to_lir-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -619,13 +620,31 @@ static bool ArmUseRelativeCall(CompilationUnit* cu, const MethodReference& targe * Bit of a hack here - in the absence of a real scheduling pass, * emit the next instruction in static & direct invoke sequences. */ -int ArmMir2Lir::ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, +int ArmMir2Lir::ArmNextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, uint32_t unused_idx ATTRIBUTE_UNUSED, uintptr_t direct_code, uintptr_t direct_method, InvokeType type) { ArmMir2Lir* cg = static_cast<ArmMir2Lir*>(cu->cg.get()); - if (direct_code != 0 && direct_method != 0) { + if (info->string_init_offset != 0) { + RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); + switch (state) { + case 0: { // Grab target method* from thread pointer + cg->LoadRefDisp(rs_rARM_SELF, info->string_init_offset, arg0_ref, kNotVolatile); + break; + } + case 1: // Grab the code from the method* + if (direct_code == 0) { + // kInvokeTgt := arg0_ref->entrypoint + cg->LoadWordDisp(arg0_ref, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmPointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt)); + } + break; + default: + return -1; + } + } else if (direct_code != 0 && direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] if (direct_code != static_cast<uintptr_t>(-1)) { diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 9a7c2ade18..e49e40d868 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -21,6 +21,7 @@ #include "arm64_lir.h" #include "base/logging.h" #include "dex/mir_graph.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/quick/mir_to_lir-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -460,7 +461,25 @@ int Arm64Mir2Lir::Arm64NextSDCallInsn(CompilationUnit* cu, CallInfo* info, InvokeType type) { UNUSED(info, unused_idx); Arm64Mir2Lir* cg = static_cast<Arm64Mir2Lir*>(cu->cg.get()); - if (direct_code != 0 && direct_method != 0) { + if (info->string_init_offset != 0) { + RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); + switch (state) { + case 0: { // Grab target method* from thread pointer + cg->LoadRefDisp(rs_xSELF, info->string_init_offset, arg0_ref, kNotVolatile); + break; + } + case 1: // Grab the code from the method* + if (direct_code == 0) { + // kInvokeTgt := arg0_ref->entrypoint + cg->LoadWordDisp(arg0_ref, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64PointerSize).Int32Value(), cg->TargetPtrReg(kInvokeTgt)); + } + break; + default: + return -1; + } + } else if (direct_code != 0 && direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] if (direct_code != static_cast<uintptr_t>(-1)) { diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index f5e6c09dba..2568ee3064 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -55,8 +55,12 @@ static constexpr bool kIntrinsicIsStatic[] = { false, // kIntrinsicReferenceGetReferent false, // kIntrinsicCharAt false, // kIntrinsicCompareTo + false, // kIntrinsicGetCharsNoCheck false, // kIntrinsicIsEmptyOrLength false, // kIntrinsicIndexOf + true, // kIntrinsicNewStringFromBytes + true, // kIntrinsicNewStringFromChars + true, // kIntrinsicNewStringFromString true, // kIntrinsicCurrentThread true, // kIntrinsicPeek true, // kIntrinsicPoke @@ -88,8 +92,15 @@ static_assert(kIntrinsicIsStatic[kIntrinsicRoundDouble], "RoundDouble must be st static_assert(!kIntrinsicIsStatic[kIntrinsicReferenceGetReferent], "Get must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicCharAt], "CharAt must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicCompareTo], "CompareTo must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicGetCharsNoCheck], "GetCharsNoCheck must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicIsEmptyOrLength], "IsEmptyOrLength must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicIndexOf], "IndexOf must not be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromBytes], + "NewStringFromBytes must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromChars], + "NewStringFromChars must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicNewStringFromString], + "NewStringFromString must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicCurrentThread], "CurrentThread must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicPeek], "Peek must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static"); @@ -137,9 +148,15 @@ const char* const DexFileMethodInliner::kClassCacheNames[] = { "F", // kClassCacheFloat "D", // kClassCacheDouble "V", // kClassCacheVoid + "[B", // kClassCacheJavaLangByteArray + "[C", // kClassCacheJavaLangCharArray + "[I", // kClassCacheJavaLangIntArray "Ljava/lang/Object;", // kClassCacheJavaLangObject - "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference + "Ljava/lang/ref/Reference;", // kClassCacheJavaLangRefReference "Ljava/lang/String;", // kClassCacheJavaLangString + "Ljava/lang/StringBuffer;", // kClassCacheJavaLangStringBuffer + "Ljava/lang/StringBuilder;", // kClassCacheJavaLangStringBuilder + "Ljava/lang/StringFactory;", // kClassCacheJavaLangStringFactory "Ljava/lang/Double;", // kClassCacheJavaLangDouble "Ljava/lang/Float;", // kClassCacheJavaLangFloat "Ljava/lang/Integer;", // kClassCacheJavaLangInteger @@ -148,10 +165,10 @@ const char* const DexFileMethodInliner::kClassCacheNames[] = { "Ljava/lang/Math;", // kClassCacheJavaLangMath "Ljava/lang/StrictMath;", // kClassCacheJavaLangStrictMath "Ljava/lang/Thread;", // kClassCacheJavaLangThread + "Ljava/nio/charset/Charset;", // kClassCacheJavaNioCharsetCharset "Llibcore/io/Memory;", // kClassCacheLibcoreIoMemory "Lsun/misc/Unsafe;", // kClassCacheSunMiscUnsafe "Ljava/lang/System;", // kClassCacheJavaLangSystem - "[C" // kClassCacheJavaLangCharArray }; const char* const DexFileMethodInliner::kNameCacheNames[] = { @@ -172,9 +189,14 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "getReferent", // kNameCacheReferenceGet "charAt", // kNameCacheCharAt "compareTo", // kNameCacheCompareTo + "getCharsNoCheck", // kNameCacheGetCharsNoCheck "isEmpty", // kNameCacheIsEmpty "indexOf", // kNameCacheIndexOf "length", // kNameCacheLength + "<init>", // kNameCacheInit + "newStringFromBytes", // kNameCacheNewStringFromBytes + "newStringFromChars", // kNameCacheNewStringFromChars + "newStringFromString", // kNameCacheNewStringFromString "currentThread", // kNameCacheCurrentThread "peekByte", // kNameCachePeekByte "peekIntNative", // kNameCachePeekIntNative @@ -282,7 +304,53 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { kClassCacheJavaLangObject } }, // kProtoCacheCharArrayICharArrayII_V { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt, - kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt}} + kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} }, + // kProtoCacheIICharArrayI_V + { kClassCacheVoid, 4, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray, + kClassCacheInt } }, + // kProtoCacheByteArrayIII_String + { kClassCacheJavaLangString, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt, + kClassCacheInt } }, + // kProtoCacheIICharArray_String + { kClassCacheJavaLangString, 3, { kClassCacheInt, kClassCacheInt, + kClassCacheJavaLangCharArray } }, + // kProtoCacheString_String + { kClassCacheJavaLangString, 1, { kClassCacheJavaLangString } }, + // kProtoCache_V + { kClassCacheVoid, 0, { } }, + // kProtoCacheByteArray_V + { kClassCacheVoid, 1, { kClassCacheJavaLangByteArray } }, + // kProtoCacheByteArrayI_V + { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheInt } }, + // kProtoCacheByteArrayII_V + { kClassCacheVoid, 3, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt } }, + // kProtoCacheByteArrayIII_V + { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt, + kClassCacheInt } }, + // kProtoCacheByteArrayIIString_V + { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt, + kClassCacheJavaLangString } }, + // kProtoCacheByteArrayString_V + { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheJavaLangString } }, + // kProtoCacheByteArrayIICharset_V + { kClassCacheVoid, 4, { kClassCacheJavaLangByteArray, kClassCacheInt, kClassCacheInt, + kClassCacheJavaNioCharsetCharset } }, + // kProtoCacheByteArrayCharset_V + { kClassCacheVoid, 2, { kClassCacheJavaLangByteArray, kClassCacheJavaNioCharsetCharset } }, + // kProtoCacheCharArray_V + { kClassCacheVoid, 1, { kClassCacheJavaLangCharArray } }, + // kProtoCacheCharArrayII_V + { kClassCacheVoid, 3, { kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt } }, + // kProtoCacheIICharArray_V + { kClassCacheVoid, 3, { kClassCacheInt, kClassCacheInt, kClassCacheJavaLangCharArray } }, + // kProtoCacheIntArrayII_V + { kClassCacheVoid, 3, { kClassCacheJavaLangIntArray, kClassCacheInt, kClassCacheInt } }, + // kProtoCacheString_V + { kClassCacheVoid, 1, { kClassCacheJavaLangString } }, + // kProtoCacheStringBuffer_V + { kClassCacheVoid, 1, { kClassCacheJavaLangStringBuffer } }, + // kProtoCacheStringBuilder_V + { kClassCacheVoid, 1, { kClassCacheJavaLangStringBuilder } }, }; const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = { @@ -343,6 +411,7 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangString, CharAt, I_C, kIntrinsicCharAt, 0), INTRINSIC(JavaLangString, CompareTo, String_I, kIntrinsicCompareTo, 0), + INTRINSIC(JavaLangString, GetCharsNoCheck, IICharArrayI_V, kIntrinsicGetCharsNoCheck, 0), INTRINSIC(JavaLangString, IsEmpty, _Z, kIntrinsicIsEmptyOrLength, kIntrinsicFlagIsEmpty), INTRINSIC(JavaLangString, IndexOf, II_I, kIntrinsicIndexOf, kIntrinsicFlagNone), INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), @@ -386,8 +455,29 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray, 0), - #undef INTRINSIC + +#define SPECIAL(c, n, p, o, d) \ + { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineSpecial, { d } } } + + SPECIAL(JavaLangString, Init, _V, kInlineStringInit, 0), + SPECIAL(JavaLangString, Init, ByteArray_V, kInlineStringInit, 1), + SPECIAL(JavaLangString, Init, ByteArrayI_V, kInlineStringInit, 2), + SPECIAL(JavaLangString, Init, ByteArrayII_V, kInlineStringInit, 3), + SPECIAL(JavaLangString, Init, ByteArrayIII_V, kInlineStringInit, 4), + SPECIAL(JavaLangString, Init, ByteArrayIIString_V, kInlineStringInit, 5), + SPECIAL(JavaLangString, Init, ByteArrayString_V, kInlineStringInit, 6), + SPECIAL(JavaLangString, Init, ByteArrayIICharset_V, kInlineStringInit, 7), + SPECIAL(JavaLangString, Init, ByteArrayCharset_V, kInlineStringInit, 8), + SPECIAL(JavaLangString, Init, CharArray_V, kInlineStringInit, 9), + SPECIAL(JavaLangString, Init, CharArrayII_V, kInlineStringInit, 10), + SPECIAL(JavaLangString, Init, IICharArray_V, kInlineStringInit, 11), + SPECIAL(JavaLangString, Init, IntArrayII_V, kInlineStringInit, 12), + SPECIAL(JavaLangString, Init, String_V, kInlineStringInit, 13), + SPECIAL(JavaLangString, Init, StringBuffer_V, kInlineStringInit, 14), + SPECIAL(JavaLangString, Init, StringBuilder_V, kInlineStringInit, 15), + +#undef SPECIAL }; DexFileMethodInliner::DexFileMethodInliner() @@ -491,11 +581,19 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { return backend->GenInlinedCharAt(info); case kIntrinsicCompareTo: return backend->GenInlinedStringCompareTo(info); + case kIntrinsicGetCharsNoCheck: + return backend->GenInlinedStringGetCharsNoCheck(info); case kIntrinsicIsEmptyOrLength: return backend->GenInlinedStringIsEmptyOrLength( info, intrinsic.d.data & kIntrinsicFlagIsEmpty); case kIntrinsicIndexOf: return backend->GenInlinedIndexOf(info, intrinsic.d.data & kIntrinsicFlagBase0); + case kIntrinsicNewStringFromBytes: + return backend->GenInlinedStringFactoryNewStringFromBytes(info); + case kIntrinsicNewStringFromChars: + return backend->GenInlinedStringFactoryNewStringFromChars(info); + case kIntrinsicNewStringFromString: + return backend->GenInlinedStringFactoryNewStringFromString(info); case kIntrinsicCurrentThread: return backend->GenInlinedCurrentThread(info); case kIntrinsicPeek: @@ -574,6 +672,8 @@ bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* i move_result = mir_graph->FindMoveResult(bb, invoke); result = GenInlineIPut(mir_graph, bb, invoke, move_result, method); break; + case kInlineStringInit: + return false; default: LOG(FATAL) << "Unexpected inline op: " << method.opcode; break; @@ -921,4 +1021,21 @@ bool DexFileMethodInliner::GenInlineIPut(MIRGraph* mir_graph, BasicBlock* bb, MI return true; } +uint32_t DexFileMethodInliner::GetOffsetForStringInit(uint32_t method_index, size_t pointer_size) { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = inline_methods_.find(method_index); + if (it != inline_methods_.end() && (it->second.opcode == kInlineStringInit)) { + uint32_t string_init_base_offset = Thread::QuickEntryPointOffsetWithSize( + OFFSETOF_MEMBER(QuickEntryPoints, pNewEmptyString), pointer_size); + return string_init_base_offset + it->second.d.data * pointer_size; + } + return 0; +} + +bool DexFileMethodInliner::IsStringInitMethodIndex(uint32_t method_index) { + ReaderMutexLock mu(Thread::Current(), lock_); + auto it = inline_methods_.find(method_index); + return (it != inline_methods_.end()) && (it->second.opcode == kInlineStringInit); +} + } // namespace art diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index d1e562119c..26b41bf54d 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -96,6 +96,17 @@ class DexFileMethodInliner { LOCKS_EXCLUDED(lock_); /** + * Gets the thread pointer entrypoint offset for a string init method index and pointer size. + */ + uint32_t GetOffsetForStringInit(uint32_t method_index, size_t pointer_size) + LOCKS_EXCLUDED(lock_); + + /** + * Check whether a particular method index is a string init. + */ + bool IsStringInitMethodIndex(uint32_t method_index) LOCKS_EXCLUDED(lock_); + + /** * To avoid multiple lookups of a class by its descriptor, we cache its * type index in the IndexCache. These are the indexes into the IndexCache * class_indexes array. @@ -111,9 +122,15 @@ class DexFileMethodInliner { kClassCacheFloat, kClassCacheDouble, kClassCacheVoid, + kClassCacheJavaLangByteArray, + kClassCacheJavaLangCharArray, + kClassCacheJavaLangIntArray, kClassCacheJavaLangObject, kClassCacheJavaLangRefReference, kClassCacheJavaLangString, + kClassCacheJavaLangStringBuffer, + kClassCacheJavaLangStringBuilder, + kClassCacheJavaLangStringFactory, kClassCacheJavaLangDouble, kClassCacheJavaLangFloat, kClassCacheJavaLangInteger, @@ -122,10 +139,10 @@ class DexFileMethodInliner { kClassCacheJavaLangMath, kClassCacheJavaLangStrictMath, kClassCacheJavaLangThread, + kClassCacheJavaNioCharsetCharset, kClassCacheLibcoreIoMemory, kClassCacheSunMiscUnsafe, kClassCacheJavaLangSystem, - kClassCacheJavaLangCharArray, kClassCacheLast }; @@ -153,9 +170,14 @@ class DexFileMethodInliner { kNameCacheReferenceGetReferent, kNameCacheCharAt, kNameCacheCompareTo, + kNameCacheGetCharsNoCheck, kNameCacheIsEmpty, kNameCacheIndexOf, kNameCacheLength, + kNameCacheInit, + kNameCacheNewStringFromBytes, + kNameCacheNewStringFromChars, + kNameCacheNewStringFromString, kNameCacheCurrentThread, kNameCachePeekByte, kNameCachePeekIntNative, @@ -230,6 +252,26 @@ class DexFileMethodInliner { kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, kProtoCacheCharArrayICharArrayII_V, + kProtoCacheIICharArrayI_V, + kProtoCacheByteArrayIII_String, + kProtoCacheIICharArray_String, + kProtoCacheString_String, + kProtoCache_V, + kProtoCacheByteArray_V, + kProtoCacheByteArrayI_V, + kProtoCacheByteArrayII_V, + kProtoCacheByteArrayIII_V, + kProtoCacheByteArrayIIString_V, + kProtoCacheByteArrayString_V, + kProtoCacheByteArrayIICharset_V, + kProtoCacheByteArrayCharset_V, + kProtoCacheCharArray_V, + kProtoCacheCharArrayII_V, + kProtoCacheIICharArray_V, + kProtoCacheIntArrayII_V, + kProtoCacheString_V, + kProtoCacheStringBuffer_V, + kProtoCacheStringBuilder_V, kProtoCacheLast }; diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 1eb3a5f1b5..ab011fc0b2 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -375,6 +375,18 @@ void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation( CallHelper(r_tgt, trampoline, safepoint_pc); } +void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocationRegLocation( + QuickEntrypointEnum trampoline, RegLocation arg0, RegLocation arg1, RegLocation arg2, + RegLocation arg3, bool safepoint_pc) { + RegStorage r_tgt = CallHelperSetup(trampoline); + LoadValueDirectFixed(arg0, TargetReg(kArg0, arg0)); + LoadValueDirectFixed(arg1, TargetReg(kArg1, arg1)); + LoadValueDirectFixed(arg2, TargetReg(kArg2, arg2)); + LoadValueDirectFixed(arg3, TargetReg(kArg3, arg3)); + ClobberCallerSave(); + CallHelper(r_tgt, trampoline, safepoint_pc); +} + /* * If there are any ins passed in registers that have not been promoted * to a callee-save register, flush them to the frame. Perform initial @@ -966,14 +978,10 @@ bool Mir2Lir::GenInlinedReferenceGetReferent(CallInfo* info) { } bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { - // Location of reference to data array + // Location of char array data int value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count int count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array - int offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_ - int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); RegLocation rl_obj = info->args[0]; RegLocation rl_idx = info->args[1]; @@ -983,38 +991,21 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { GenNullCheck(rl_obj.reg, info->opt_flags); bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK)); LIR* range_check_branch = nullptr; - RegStorage reg_off; - RegStorage reg_ptr; - reg_off = AllocTemp(); - reg_ptr = AllocTempRef(); if (range_check) { reg_max = AllocTemp(); Load32Disp(rl_obj.reg, count_offset, reg_max); MarkPossibleNullPointerException(info->opt_flags); - } - Load32Disp(rl_obj.reg, offset_offset, reg_off); - MarkPossibleNullPointerException(info->opt_flags); - LoadRefDisp(rl_obj.reg, value_offset, reg_ptr, kNotVolatile); - if (range_check) { - // Set up a slow path to allow retry in case of bounds violation */ + // Set up a slow path to allow retry in case of bounds violation OpRegReg(kOpCmp, rl_idx.reg, reg_max); FreeTemp(reg_max); range_check_branch = OpCondBranch(kCondUge, nullptr); } - OpRegImm(kOpAdd, reg_ptr, data_offset); - if (rl_idx.is_const) { - OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg)); - } else { - OpRegReg(kOpAdd, reg_off, rl_idx.reg); - } + RegStorage reg_ptr = AllocTempRef(); + OpRegRegImm(kOpAdd, reg_ptr, rl_obj.reg, value_offset); FreeTemp(rl_obj.reg); - if (rl_idx.location == kLocPhysReg) { - FreeTemp(rl_idx.reg); - } RegLocation rl_dest = InlineTarget(info); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - LoadBaseIndexed(reg_ptr, reg_off, rl_result.reg, 1, kUnsignedHalf); - FreeTemp(reg_off); + LoadBaseIndexed(reg_ptr, rl_idx.reg, rl_result.reg, 1, kUnsignedHalf); FreeTemp(reg_ptr); StoreValue(rl_dest, rl_result); if (range_check) { @@ -1025,6 +1016,59 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) { return true; } +bool Mir2Lir::GenInlinedStringGetCharsNoCheck(CallInfo* info) { + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation + return false; + } + size_t char_component_size = Primitive::ComponentSize(Primitive::kPrimChar); + // Location of data in char array buffer + int data_offset = mirror::Array::DataOffset(char_component_size).Int32Value(); + // Location of char array data in string + int value_offset = mirror::String::ValueOffset().Int32Value(); + + RegLocation rl_obj = info->args[0]; + RegLocation rl_start = info->args[1]; + RegLocation rl_end = info->args[2]; + RegLocation rl_buffer = info->args[3]; + RegLocation rl_index = info->args[4]; + + ClobberCallerSave(); + LockCallTemps(); // Using fixed registers + RegStorage reg_dst_ptr = TargetReg(kArg0, kRef); + RegStorage reg_src_ptr = TargetReg(kArg1, kRef); + RegStorage reg_length = TargetReg(kArg2, kNotWide); + RegStorage reg_tmp = TargetReg(kArg3, kNotWide); + RegStorage reg_tmp_ptr = RegStorage(RegStorage::k64BitSolo, reg_tmp.GetRawBits() & RegStorage::kRegTypeMask); + + LoadValueDirectFixed(rl_buffer, reg_dst_ptr); + OpRegImm(kOpAdd, reg_dst_ptr, data_offset); + LoadValueDirectFixed(rl_index, reg_tmp); + OpRegRegImm(kOpLsl, reg_tmp, reg_tmp, 1); + OpRegReg(kOpAdd, reg_dst_ptr, cu_->instruction_set == kArm64 ? reg_tmp_ptr : reg_tmp); + + LoadValueDirectFixed(rl_start, reg_tmp); + LoadValueDirectFixed(rl_end, reg_length); + OpRegReg(kOpSub, reg_length, reg_tmp); + OpRegRegImm(kOpLsl, reg_length, reg_length, 1); + LoadValueDirectFixed(rl_obj, reg_src_ptr); + + OpRegImm(kOpAdd, reg_src_ptr, value_offset); + OpRegRegImm(kOpLsl, reg_tmp, reg_tmp, 1); + OpRegReg(kOpAdd, reg_src_ptr, cu_->instruction_set == kArm64 ? reg_tmp_ptr : reg_tmp); + + RegStorage r_tgt; + if (cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64) { + r_tgt = LoadHelper(kQuickMemcpy); + } else { + r_tgt = RegStorage::InvalidReg(); + } + // NOTE: not a safepoint + CallHelper(r_tgt, kQuickMemcpy, false, true); + + return true; +} + // Generates an inlined String.is_empty or String.length. bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { if (cu_->instruction_set == kMips || cu_->instruction_set == kMips64) { @@ -1058,6 +1102,58 @@ bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) { return true; } +bool Mir2Lir::GenInlinedStringFactoryNewStringFromBytes(CallInfo* info) { + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation + return false; + } + RegLocation rl_data = info->args[0]; + RegLocation rl_high = info->args[1]; + RegLocation rl_offset = info->args[2]; + RegLocation rl_count = info->args[3]; + rl_data = LoadValue(rl_data, kRefReg); + LIR* data_null_check_branch = OpCmpImmBranch(kCondEq, rl_data.reg, 0, nullptr); + AddIntrinsicSlowPath(info, data_null_check_branch); + CallRuntimeHelperRegLocationRegLocationRegLocationRegLocation( + kQuickAllocStringFromBytes, rl_data, rl_high, rl_offset, rl_count, true); + RegLocation rl_return = GetReturn(kRefReg); + RegLocation rl_dest = InlineTarget(info); + StoreValue(rl_dest, rl_return); + return true; +} + +bool Mir2Lir::GenInlinedStringFactoryNewStringFromChars(CallInfo* info) { + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation + return false; + } + RegLocation rl_offset = info->args[0]; + RegLocation rl_count = info->args[1]; + RegLocation rl_data = info->args[2]; + CallRuntimeHelperRegLocationRegLocationRegLocation( + kQuickAllocStringFromChars, rl_offset, rl_count, rl_data, true); + RegLocation rl_return = GetReturn(kRefReg); + RegLocation rl_dest = InlineTarget(info); + StoreValue(rl_dest, rl_return); + return true; +} + +bool Mir2Lir::GenInlinedStringFactoryNewStringFromString(CallInfo* info) { + if (cu_->instruction_set == kMips) { + // TODO - add Mips implementation + return false; + } + RegLocation rl_string = info->args[0]; + rl_string = LoadValue(rl_string, kRefReg); + LIR* string_null_check_branch = OpCmpImmBranch(kCondEq, rl_string.reg, 0, nullptr); + AddIntrinsicSlowPath(info, string_null_check_branch); + CallRuntimeHelperRegLocation(kQuickAllocStringFromString, rl_string, true); + RegLocation rl_return = GetReturn(kRefReg); + RegLocation rl_dest = InlineTarget(info); + StoreValue(rl_dest, rl_return); + return true; +} + bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) { if (cu_->instruction_set == kMips || cu_->instruction_set == kMips64) { // TODO: add Mips and Mips64 implementations. @@ -1451,9 +1547,22 @@ void Mir2Lir::GenInvokeNoInline(CallInfo* info) { LockCallTemps(); const MirMethodLoweringInfo& method_info = mir_graph_->GetMethodLoweringInfo(info->mir); + MethodReference target_method = method_info.GetTargetMethod(); cu_->compiler_driver->ProcessedInvoke(method_info.GetInvokeType(), method_info.StatsFlags()); InvokeType original_type = static_cast<InvokeType>(method_info.GetInvokeType()); info->type = method_info.GetSharpType(); + bool is_string_init = false; + if (method_info.IsSpecial()) { + DexFileMethodInliner* inliner = cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner( + target_method.dex_file); + if (inliner->IsStringInitMethodIndex(target_method.dex_method_index)) { + is_string_init = true; + size_t pointer_size = GetInstructionSetPointerSize(cu_->instruction_set); + info->string_init_offset = inliner->GetOffsetForStringInit(target_method.dex_method_index, + pointer_size); + info->type = kStatic; + } + } bool fast_path = method_info.FastPath(); bool skip_this; @@ -1478,7 +1587,6 @@ void Mir2Lir::GenInvokeNoInline(CallInfo* info) { next_call_insn = fast_path ? NextVCallInsn : NextVCallInsnSP; skip_this = fast_path; } - MethodReference target_method = method_info.GetTargetMethod(); call_state = GenDalvikArgs(info, call_state, p_null_ck, next_call_insn, target_method, method_info.VTableIndex(), method_info.DirectCode(), method_info.DirectMethod(), @@ -1495,7 +1603,7 @@ void Mir2Lir::GenInvokeNoInline(CallInfo* info) { FreeCallTemps(); if (info->result.location != kLocInvalid) { // We have a following MOVE_RESULT - do it now. - RegisterClass reg_class = + RegisterClass reg_class = is_string_init ? kRefReg : ShortyToRegClass(mir_graph_->GetShortyFromMethodReference(info->method_ref)[0]); if (info->result.wide) { RegLocation ret_loc = GetReturnWide(reg_class); diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 39b9cc7056..3d253842c9 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -20,7 +20,9 @@ #include "base/logging.h" #include "dex/mir_graph.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/quick/mir_to_lir-inl.h" +#include "driver/compiler_driver.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" #include "mips_lir.h" @@ -397,11 +399,28 @@ void MipsMir2Lir::GenSpecialExitForSuspend() { * Bit of a hack here - in the absence of a real scheduling pass, * emit the next instruction in static & direct invoke sequences. */ -static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, int state, +static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, uint32_t, uintptr_t direct_code, uintptr_t direct_method, InvokeType type) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); - if (direct_code != 0 && direct_method != 0) { + if (info->string_init_offset != 0) { + RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); + switch (state) { + case 0: { // Grab target method* from thread pointer + cg->LoadRefDisp(cg->TargetPtrReg(kSelf), info->string_init_offset, arg0_ref, kNotVolatile); + break; + } + case 1: // Grab the code from the method* + if (direct_code == 0) { + int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + InstructionSetPointerSize(cu->instruction_set)).Int32Value(); + cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt)); + } + break; + default: + return -1; + } + } else if (direct_code != 0 && direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] if (direct_code != static_cast<uintptr_t>(-1)) { diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 6f227fcee3..4fdc7289bf 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -897,6 +897,10 @@ class Mir2Lir { RegLocation arg0, RegLocation arg1, RegLocation arg2, bool safepoint_pc); + void CallRuntimeHelperRegLocationRegLocationRegLocationRegLocation( + QuickEntrypointEnum trampoline, RegLocation arg0, RegLocation arg1, + RegLocation arg2, RegLocation arg3, bool safepoint_pc); + void GenInvoke(CallInfo* info); void GenInvokeNoInline(CallInfo* info); virtual NextCallInsn GetNextSDCallInsn() = 0; @@ -937,7 +941,11 @@ class Mir2Lir { bool GenInlinedReferenceGetReferent(CallInfo* info); virtual bool GenInlinedCharAt(CallInfo* info); + bool GenInlinedStringGetCharsNoCheck(CallInfo* info); bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty); + bool GenInlinedStringFactoryNewStringFromBytes(CallInfo* info); + bool GenInlinedStringFactoryNewStringFromChars(CallInfo* info); + bool GenInlinedStringFactoryNewStringFromString(CallInfo* info); virtual bool GenInlinedReverseBits(CallInfo* info, OpSize size); bool GenInlinedReverseBytes(CallInfo* info, OpSize size); virtual bool GenInlinedAbsInt(CallInfo* info); @@ -1459,26 +1467,6 @@ class Mir2Lir { return InexpensiveConstantInt(value); } - /** - * @brief Whether division by the given divisor can be converted to multiply by its reciprocal. - * @param divisor A constant divisor bits of float type. - * @return Returns true iff, x/divisor == x*(1.0f/divisor), for every float x. - */ - bool CanDivideByReciprocalMultiplyFloat(int32_t divisor) { - // True, if float value significand bits are 0. - return ((divisor & 0x7fffff) == 0); - } - - /** - * @brief Whether division by the given divisor can be converted to multiply by its reciprocal. - * @param divisor A constant divisor bits of double type. - * @return Returns true iff, x/divisor == x*(1.0/divisor), for every double x. - */ - bool CanDivideByReciprocalMultiplyDouble(int64_t divisor) { - // True, if double value significand bits are 0. - return ((divisor & ((UINT64_C(1) << 52) - 1)) == 0); - } - // May be optimized by targets. virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src); virtual void GenMonitorExit(int opt_flags, RegLocation rl_src); diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 39eb117e9c..73cfe92c45 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -575,7 +575,7 @@ static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimi // (1 << kNullCheckElimination) | // (1 << kClassInitCheckElimination) | // (1 << kGlobalValueNumbering) | - (1 << kGvnDeadCodeElimination) | + // (1 << kGvnDeadCodeElimination) | // (1 << kLocalValueNumbering) | // (1 << kPromoteRegs) | // (1 << kTrackLiveTemps) | diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index e2364d8548..249575761e 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -19,6 +19,7 @@ #include "codegen_x86.h" #include "base/logging.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "dex/quick/mir_to_lir-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -343,11 +344,20 @@ void X86Mir2Lir::GenImplicitNullCheck(RegStorage reg, int opt_flags) { int X86Mir2Lir::X86NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state, const MethodReference& target_method, uint32_t, - uintptr_t direct_code, uintptr_t direct_method, + uintptr_t direct_code ATTRIBUTE_UNUSED, uintptr_t direct_method, InvokeType type) { - UNUSED(info, direct_code); X86Mir2Lir* cg = static_cast<X86Mir2Lir*>(cu->cg.get()); - if (direct_method != 0) { + if (info->string_init_offset != 0) { + RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); + switch (state) { + case 0: { // Grab target method* from thread pointer + cg->NewLIR2(kX86Mov32RT, arg0_ref.GetReg(), info->string_init_offset); + break; + } + default: + return -1; + } + } else if (direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] if (direct_method != static_cast<uintptr_t>(-1)) { diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index b4603793b4..2f211da264 100755 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -1302,10 +1302,6 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { int value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count within the String object. int count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array. - int offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_. - int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); // Compute the number of words to search in to rCX. Load32Disp(rs_rDX, count_offset, rs_rCX); @@ -1388,15 +1384,13 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { // Load the address of the string into EDI. // In case of start index we have to add the address to existing value in EDI. - // The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET. if (zero_based || (!zero_based && rl_start.is_const && start_value == 0)) { - Load32Disp(rs_rDX, offset_offset, rs_rDI); + OpRegRegImm(kOpAdd, rs_rDI, rs_rDX, value_offset); } else { - OpRegMem(kOpAdd, rs_rDI, rs_rDX, offset_offset); + OpRegImm(kOpLsl, rs_rDI, 1); + OpRegReg(kOpAdd, rs_rDI, rs_rDX); + OpRegImm(kOpAdd, rs_rDI, value_offset); } - OpRegImm(kOpLsl, rs_rDI, 1); - OpRegMem(kOpAdd, rs_rDI, rs_rDX, value_offset); - OpRegImm(kOpAdd, rs_rDI, data_offset); // EDI now contains the start of the string to be searched. // We are all prepared to do the search for the character. @@ -2423,24 +2417,15 @@ bool X86Mir2Lir::GenInlinedCharAt(CallInfo* info) { int value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count int count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array - int offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_ - int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); RegLocation rl_obj = info->args[0]; RegLocation rl_idx = info->args[1]; rl_obj = LoadValue(rl_obj, kRefReg); - // X86 wants to avoid putting a constant index into a register. - if (!rl_idx.is_const) { - rl_idx = LoadValue(rl_idx, kCoreReg); - } + rl_idx = LoadValue(rl_idx, kCoreReg); RegStorage reg_max; GenNullCheck(rl_obj.reg, info->opt_flags); bool range_check = (!(info->opt_flags & MIR_IGNORE_RANGE_CHECK)); LIR* range_check_branch = nullptr; - RegStorage reg_off; - RegStorage reg_ptr; if (range_check) { // On x86, we can compare to memory directly // Set up a launch pad to allow retry in case of bounds violation */ @@ -2456,24 +2441,11 @@ bool X86Mir2Lir::GenInlinedCharAt(CallInfo* info) { range_check_branch = OpCondBranch(kCondUge, nullptr); } } - reg_off = AllocTemp(); - reg_ptr = AllocTempRef(); - Load32Disp(rl_obj.reg, offset_offset, reg_off); - LoadRefDisp(rl_obj.reg, value_offset, reg_ptr, kNotVolatile); - if (rl_idx.is_const) { - OpRegImm(kOpAdd, reg_off, mir_graph_->ConstantValue(rl_idx.orig_sreg)); - } else { - OpRegReg(kOpAdd, reg_off, rl_idx.reg); - } - FreeTemp(rl_obj.reg); - if (rl_idx.location == kLocPhysReg) { - FreeTemp(rl_idx.reg); - } RegLocation rl_dest = InlineTarget(info); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - LoadBaseIndexedDisp(reg_ptr, reg_off, 1, data_offset, rl_result.reg, kUnsignedHalf); - FreeTemp(reg_off); - FreeTemp(reg_ptr); + LoadBaseIndexedDisp(rl_obj.reg, rl_idx.reg, 1, value_offset, rl_result.reg, kUnsignedHalf); + FreeTemp(rl_idx.reg); + FreeTemp(rl_obj.reg); StoreValue(rl_dest, rl_result); if (range_check) { DCHECK(range_check_branch != nullptr); diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index 7eba515200..e788261ad0 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -64,6 +64,9 @@ const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_ve if (method_verifier->HasCheckCasts()) { verified_method->GenerateSafeCastSet(method_verifier); } + + verified_method->SetStringInitPcRegMap(method_verifier->GetStringInitPcRegMap()); + return verified_method.release(); } diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h index ad07639b1c..242e3dfe6e 100644 --- a/compiler/dex/verified_method.h +++ b/compiler/dex/verified_method.h @@ -75,6 +75,13 @@ class VerifiedMethod { return has_verification_failures_; } + void SetStringInitPcRegMap(SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map) { + string_init_pc_reg_map_ = string_init_pc_reg_map; + } + const SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() const { + return string_init_pc_reg_map_; + } + private: VerifiedMethod() = default; @@ -114,6 +121,10 @@ class VerifiedMethod { SafeCastSet safe_cast_set_; bool has_verification_failures_; + + // Copy of mapping generated by verifier of dex PCs of string init invocations + // to the set of other registers that the receiver has been copied into. + SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_; }; } // namespace art diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c858326562..47288b5098 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -40,6 +40,7 @@ #include "dex/verification_results.h" #include "dex/verified_method.h" #include "dex/quick/dex_file_method_inliner.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_options.h" #include "elf_writer_quick.h" #include "jni_internal.h" @@ -2485,4 +2486,16 @@ std::string CompilerDriver::GetMemoryUsageString(bool extended) const { return oss.str(); } +bool CompilerDriver::IsStringTypeIndex(uint16_t type_index, const DexFile* dex_file) { + const char* type = dex_file->GetTypeDescriptor(dex_file->GetTypeId(type_index)); + return strcmp(type, "Ljava/lang/String;") == 0; +} + +bool CompilerDriver::IsStringInit(uint32_t method_index, const DexFile* dex_file, int32_t* offset) { + DexFileMethodInliner* inliner = GetMethodInlinerMap()->GetMethodInliner(dex_file); + size_t pointer_size = InstructionSetPointerSize(GetInstructionSet()); + *offset = inliner->GetOffsetForStringInit(method_index, pointer_size); + return inliner->IsStringInitMethodIndex(method_index); +} + } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 02de11e960..2b0985a77e 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -466,6 +466,9 @@ class CompilerDriver { // Get memory usage during compilation. std::string GetMemoryUsageString(bool extended) const; + bool IsStringTypeIndex(uint16_t type_index, const DexFile* dex_file); + bool IsStringInit(uint32_t method_index, const DexFile* dex_file, int32_t* offset); + void SetHadHardVerifierFailure() { had_hard_verifier_failure_ = true; } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index fc70d8f998..4dc75091e7 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -89,7 +89,12 @@ bool ImageWriter::PrepareImageAddressSpace() { Thread::Current()->TransitionFromSuspendedToRunnable(); PruneNonImageClasses(); // Remove junk ComputeLazyFieldsForImageClasses(); // Add useful information - ProcessStrings(); + + // Calling this can in theory fill in some resolved strings. However, in practice it seems to + // never resolve any. + if (kComputeEagerResolvedStrings) { + ComputeEagerResolvedStrings(); + } Thread::Current()->TransitionFromRunnableToSuspended(kNative); } gc::Heap* heap = Runtime::Current()->GetHeap(); @@ -529,14 +534,6 @@ bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* c, void* /*arg*/) { return true; } -// Count the number of strings in the heap and put the result in arg as a size_t pointer. -static void CountStringsCallback(Object* obj, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (obj->GetClass()->IsStringClass()) { - ++*reinterpret_cast<size_t*>(arg); - } -} - // Collect all the java.lang.String in the heap and put them in the output strings_ array. class StringCollector { public: @@ -566,99 +563,19 @@ class LexicographicalStringComparator { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::String* lhs_s = lhs.AsMirrorPtr(); mirror::String* rhs_s = rhs.AsMirrorPtr(); - uint16_t* lhs_begin = lhs_s->GetCharArray()->GetData() + lhs_s->GetOffset(); - uint16_t* rhs_begin = rhs_s->GetCharArray()->GetData() + rhs_s->GetOffset(); + uint16_t* lhs_begin = lhs_s->GetValue(); + uint16_t* rhs_begin = rhs_s->GetValue(); return std::lexicographical_compare(lhs_begin, lhs_begin + lhs_s->GetLength(), rhs_begin, rhs_begin + rhs_s->GetLength()); } }; -static bool IsPrefix(mirror::String* pref, mirror::String* full) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (pref->GetLength() > full->GetLength()) { - return false; - } - uint16_t* pref_begin = pref->GetCharArray()->GetData() + pref->GetOffset(); - uint16_t* full_begin = full->GetCharArray()->GetData() + full->GetOffset(); - return std::equal(pref_begin, pref_begin + pref->GetLength(), full_begin); -} - -void ImageWriter::ProcessStrings() { - size_t total_strings = 0; - gc::Heap* heap = Runtime::Current()->GetHeap(); - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - // Count the strings. - heap->VisitObjects(CountStringsCallback, &total_strings); - Thread* self = Thread::Current(); - StackHandleScope<1> hs(self); - auto strings = hs.NewHandle(cl->AllocStringArray(self, total_strings)); - StringCollector string_collector(strings, 0U); - // Read strings into the array. - heap->VisitObjects(StringCollector::Callback, &string_collector); - // Some strings could have gotten freed if AllocStringArray caused a GC. - CHECK_LE(string_collector.GetIndex(), total_strings); - total_strings = string_collector.GetIndex(); - auto* strings_begin = reinterpret_cast<mirror::HeapReference<mirror::String>*>( - strings->GetRawData(sizeof(mirror::HeapReference<mirror::String>), 0)); - std::sort(strings_begin, strings_begin + total_strings, LexicographicalStringComparator()); - // Characters of strings which are non equal prefix of another string (not the same string). - // We don't count the savings from equal strings since these would get interned later anyways. - size_t prefix_saved_chars = 0; - // Count characters needed for the strings. - size_t num_chars = 0u; - mirror::String* prev_s = nullptr; - for (size_t idx = 0; idx != total_strings; ++idx) { - mirror::String* s = strings->GetWithoutChecks(idx); - size_t length = s->GetLength(); - num_chars += length; - if (prev_s != nullptr && IsPrefix(prev_s, s)) { - size_t prev_length = prev_s->GetLength(); - num_chars -= prev_length; - if (prev_length != length) { - prefix_saved_chars += prev_length; - } - } - prev_s = s; - } - // Create character array, copy characters and point the strings there. - mirror::CharArray* array = mirror::CharArray::Alloc(self, num_chars); - string_data_array_ = array; - uint16_t* array_data = array->GetData(); - size_t pos = 0u; - prev_s = nullptr; - for (size_t idx = 0; idx != total_strings; ++idx) { - mirror::String* s = strings->GetWithoutChecks(idx); - uint16_t* s_data = s->GetCharArray()->GetData() + s->GetOffset(); - int32_t s_length = s->GetLength(); - int32_t prefix_length = 0u; - if (idx != 0u && IsPrefix(prev_s, s)) { - prefix_length = prev_s->GetLength(); - } - memcpy(array_data + pos, s_data + prefix_length, (s_length - prefix_length) * sizeof(*s_data)); - s->SetOffset(pos - prefix_length); - s->SetArray(array); - pos += s_length - prefix_length; - prev_s = s; - } - CHECK_EQ(pos, num_chars); - - if (kIsDebugBuild || VLOG_IS_ON(compiler)) { - LOG(INFO) << "Total # image strings=" << total_strings << " combined length=" - << num_chars << " prefix saved chars=" << prefix_saved_chars; - } - // Calling this can in theory fill in some resolved strings. However, in practice it seems to - // never resolve any. - if (kComputeEagerResolvedStrings) { - ComputeEagerResolvedStrings(); - } -} - void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) { if (!obj->GetClass()->IsStringClass()) { return; } mirror::String* string = obj->AsString(); - const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); + const uint16_t* utf16_string = string->GetValue(); size_t utf16_length = static_cast<size_t>(string->GetLength()); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ReaderMutexLock mu(Thread::Current(), *class_linker->DexLock()); diff --git a/compiler/image_writer.h b/compiler/image_writer.h index a2d99eeb96..c0cffa5b2e 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -220,9 +220,6 @@ class ImageWriter FINAL { static void ComputeEagerResolvedStringsCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Combine string char arrays. - void ProcessStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Remove unwanted classes from various roots. void PruneNonImageClasses() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static bool NonImageClassesVisitor(mirror::Class* c, void* arg) diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 925b507e09..dbdcc96fc1 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -176,7 +176,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(28U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(92 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); + EXPECT_EQ(111 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index 9a9215135a..8100a29f32 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -18,6 +18,26 @@ namespace art { +void HBooleanSimplifier::TryRemovingNegatedCondition(HBasicBlock* block) { + DCHECK(block->EndsWithIf()); + + // Check if the condition is a Boolean negation. + HIf* if_instruction = block->GetLastInstruction()->AsIf(); + HInstruction* boolean_not = if_instruction->InputAt(0); + if (!boolean_not->IsBooleanNot()) { + return; + } + + // Make BooleanNot's input the condition of the If and swap branches. + if_instruction->ReplaceInput(boolean_not->InputAt(0), 0); + block->SwapSuccessors(); + + // Remove the BooleanNot if it is now unused. + if (!boolean_not->HasUses()) { + boolean_not->GetBlock()->RemoveInstruction(boolean_not); + } +} + // Returns true if 'block1' and 'block2' are empty, merge into the same single // successor and the successor can only be reached from them. static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) { @@ -78,58 +98,69 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { } } +void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) { + DCHECK(block->EndsWithIf()); + + // Find elements of the pattern. + HIf* if_instruction = block->GetLastInstruction()->AsIf(); + HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); + HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); + if (!BlocksDoMergeTogether(true_block, false_block)) { + return; + } + HBasicBlock* merge_block = true_block->GetSuccessors().Get(0); + if (!merge_block->HasSinglePhi()) { + return; + } + HPhi* phi = merge_block->GetFirstPhi()->AsPhi(); + HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block)); + HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block)); + + // Check if the selection negates/preserves the value of the condition and + // if so, generate a suitable replacement instruction. + HInstruction* if_condition = if_instruction->InputAt(0); + HInstruction* replacement; + if (NegatesCondition(true_value, false_value)) { + replacement = GetOppositeCondition(if_condition); + if (replacement->GetBlock() == nullptr) { + block->InsertInstructionBefore(replacement, if_instruction); + } + } else if (PreservesCondition(true_value, false_value)) { + replacement = if_condition; + } else { + return; + } + + // Replace the selection outcome with the new instruction. + phi->ReplaceWith(replacement); + merge_block->RemovePhi(phi); + + // Delete the true branch and merge the resulting chain of blocks + // 'block->false_block->merge_block' into one. + true_block->DisconnectAndDelete(); + block->MergeWith(false_block); + block->MergeWith(merge_block); + + // Remove the original condition if it is now unused. + if (!if_condition->HasUses()) { + if_condition->GetBlock()->RemoveInstructionOrPhi(if_condition); + } +} + void HBooleanSimplifier::Run() { // Iterate in post order in the unlikely case that removing one occurrence of - // the pattern empties a branch block of another occurrence. Otherwise the - // order does not matter. + // the selection pattern empties a branch block of another occurrence. + // Otherwise the order does not matter. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); if (!block->EndsWithIf()) continue; - // Find elements of the pattern. - HIf* if_instruction = block->GetLastInstruction()->AsIf(); - HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); - HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); - if (!BlocksDoMergeTogether(true_block, false_block)) { - continue; - } - HBasicBlock* merge_block = true_block->GetSuccessors().Get(0); - if (!merge_block->HasSinglePhi()) { - continue; - } - HPhi* phi = merge_block->GetFirstPhi()->AsPhi(); - HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block)); - HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block)); - - // Check if the selection negates/preserves the value of the condition and - // if so, generate a suitable replacement instruction. - HInstruction* if_condition = if_instruction->InputAt(0); - HInstruction* replacement; - if (NegatesCondition(true_value, false_value)) { - replacement = GetOppositeCondition(if_condition); - if (replacement->GetBlock() == nullptr) { - block->InsertInstructionBefore(replacement, if_instruction); - } - } else if (PreservesCondition(true_value, false_value)) { - replacement = if_condition; - } else { - continue; - } + // If condition is negated, remove the negation and swap the branches. + TryRemovingNegatedCondition(block); - // Replace the selection outcome with the new instruction. - phi->ReplaceWith(replacement); - merge_block->RemovePhi(phi); - - // Delete the true branch and merge the resulting chain of blocks - // 'block->false_block->merge_block' into one. - true_block->DisconnectAndDelete(); - block->MergeWith(false_block); - block->MergeWith(merge_block); - - // Remove the original condition if it is now unused. - if (!if_condition->HasUses()) { - if_condition->GetBlock()->RemoveInstructionOrPhi(if_condition); - } + // If this is a boolean-selection diamond pattern, replace its result with + // the condition value (or its negation) and simplify the graph. + TryRemovingBooleanSelection(block); } } diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h index a88733e1af..733ebaac2c 100644 --- a/compiler/optimizing/boolean_simplifier.h +++ b/compiler/optimizing/boolean_simplifier.h @@ -14,11 +14,15 @@ * limitations under the License. */ -// This optimization recognizes a common pattern where a boolean value is -// either cast to an integer or negated by selecting from zero/one integer -// constants with an If statement. Because boolean values are internally -// represented as zero/one, we can safely replace the pattern with a suitable -// condition instruction. +// This optimization recognizes two common patterns: +// (a) Boolean selection: Casting a boolean to an integer or negating it is +// carried out with an If statement selecting from zero/one integer +// constants. Because Boolean values are represented as zero/one, the +// pattern can be replaced with the condition instruction itself or its +// negation, depending on the layout. +// (b) Negated condition: Instruction simplifier may replace an If's condition +// with a boolean value. If this value is the result of a Boolean negation, +// the true/false branches can be swapped and negation removed. // Example: Negating a boolean value // B1: @@ -66,6 +70,9 @@ class HBooleanSimplifier : public HOptimization { static constexpr const char* kBooleanSimplifierPassName = "boolean_simplifier"; private: + void TryRemovingNegatedCondition(HBasicBlock* block); + void TryRemovingBooleanSelection(HBasicBlock* block); + DISALLOW_COPY_AND_ASSIGN(HBooleanSimplifier); }; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 96e08fd24c..0f44af07b8 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -19,6 +19,7 @@ #include "art_field-inl.h" #include "base/logging.h" #include "class_linker.h" +#include "dex/verified_method.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex/verified_method.h" @@ -612,6 +613,16 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, HInvokeStaticOrDirect::ClinitCheckRequirement::kImplicit; // Potential class initialization check, in the case of a static method call. HClinitCheck* clinit_check = nullptr; + // Replace calls to String.<init> with StringFactory. + int32_t string_init_offset = 0; + bool is_string_init = compiler_driver_->IsStringInit(method_idx, dex_file_, &string_init_offset); + if (is_string_init) { + return_type = Primitive::kPrimNot; + is_instance_call = false; + number_of_arguments--; + invoke_type = kStatic; + optimized_invoke_type = kStatic; + } HInvoke* invoke = nullptr; @@ -691,14 +702,14 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, current_block_->AddInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); current_block_->AddInstruction(clinit_check); - ++number_of_arguments; } } } invoke = new (arena_) HInvokeStaticOrDirect( arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index, - is_recursive, invoke_type, optimized_invoke_type, clinit_check_requirement); + is_recursive, string_init_offset, invoke_type, optimized_invoke_type, + clinit_check_requirement); } size_t start_index = 0; @@ -714,6 +725,9 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, uint32_t descriptor_index = 1; uint32_t argument_index = start_index; + if (is_string_init) { + start_index = 1; + } for (size_t i = start_index; i < number_of_vreg_arguments; i++, argument_index++) { Primitive::Type type = Primitive::GetType(descriptor[descriptor_index++]); bool is_wide = (type == Primitive::kPrimLong) || (type == Primitive::kPrimDouble); @@ -730,16 +744,38 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, i++; } } + DCHECK_EQ(argument_index, number_of_arguments); if (clinit_check_requirement == HInvokeStaticOrDirect::ClinitCheckRequirement::kExplicit) { // Add the class initialization check as last input of `invoke`. DCHECK(clinit_check != nullptr); - invoke->SetArgumentAt(argument_index++, clinit_check); + invoke->SetArgumentAt(argument_index, clinit_check); } - DCHECK_EQ(argument_index, number_of_arguments); current_block_->AddInstruction(invoke); latest_result_ = invoke; + + // Add move-result for StringFactory method. + if (is_string_init) { + uint32_t orig_this_reg = is_range ? register_index : args[0]; + const VerifiedMethod* verified_method = + compiler_driver_->GetVerifiedMethod(dex_file_, dex_compilation_unit_->GetDexMethodIndex()); + if (verified_method == nullptr) { + LOG(WARNING) << "No verified method for method calling String.<init>: " + << PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_); + return false; + } + const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map = + verified_method->GetStringInitPcRegMap(); + auto map_it = string_init_map.find(dex_pc); + if (map_it != string_init_map.end()) { + std::set<uint32_t> reg_set = map_it->second; + for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) { + UpdateLocal(*set_it, invoke); + } + } + UpdateLocal(orig_this_reg, invoke); + } return true; } @@ -1916,12 +1952,19 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::NEW_INSTANCE: { uint16_t type_index = instruction.VRegB_21c(); - QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index) - ? kQuickAllocObjectWithAccessCheck - : kQuickAllocObject; - - current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint)); - UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + if (compiler_driver_->IsStringTypeIndex(type_index, dex_file_)) { + // Turn new-instance of string into a const 0. + int32_t register_index = instruction.VRegA(); + HNullConstant* constant = graph_->GetNullConstant(); + UpdateLocal(register_index, constant); + } else { + QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index) + ? kQuickAllocObjectWithAccessCheck + : kQuickAllocObject; + + current_block_->AddInstruction(new (arena_) HNewInstance(dex_pc, type_index, entrypoint)); + UpdateLocal(instruction.VRegA(), current_block_->GetLastInstruction()); + } break; } diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 5163395cac..cfe121e0ec 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -153,6 +153,7 @@ HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const { } void CodeGenerator::CompileInternal(CodeAllocator* allocator, bool is_baseline) { + is_baseline_ = is_baseline; HGraphVisitor* instruction_visitor = GetInstructionVisitor(); DCHECK_EQ(current_block_index_, 0u); GenerateFrameEntry(); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index e536b2d0ee..6342f91684 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -212,6 +212,10 @@ class CodeGenerator { std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const; void BuildStackMaps(std::vector<uint8_t>* vector); + bool IsBaseline() const { + return is_baseline_; + } + bool IsLeafMethod() const { return is_leaf_; } @@ -325,6 +329,7 @@ class CodeGenerator { number_of_register_pairs_(number_of_register_pairs), core_callee_save_mask_(core_callee_save_mask), fpu_callee_save_mask_(fpu_callee_save_mask), + is_baseline_(false), graph_(graph), compiler_options_(compiler_options), pc_infos_(graph->GetArena(), 32), @@ -404,6 +409,9 @@ class CodeGenerator { const uint32_t core_callee_save_mask_; const uint32_t fpu_callee_save_mask_; + // Whether we are using baseline. + bool is_baseline_; + private: void InitLocationsBaseline(HInstruction* instruction); size_t GetStackOffsetOfSavedRegister(size_t index); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 01748a9f5c..159bd30e45 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1241,13 +1241,9 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { } void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderARM intrinsic(GetGraph()->GetArena(), codegen_->GetInstructionSetFeatures()); @@ -1273,9 +1269,9 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM* codegen) } void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; @@ -1293,7 +1289,7 @@ void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { locations->AddTemp(Location::RegisterLocation(R0)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -4071,15 +4067,9 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ LoadFromOffset( - kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); - // temp = temp[index_in_cache] - __ LoadFromOffset( - kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())); + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ LoadFromOffset(kLoadWord, temp, TR, invoke->GetStringInitOffset()); // LR = temp[offset_of_quick_compiled_code] __ LoadFromOffset(kLoadWord, LR, temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( @@ -4087,7 +4077,24 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, // LR() __ blx(LR); } else { - __ bl(GetFrameEntryLabel()); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ LoadFromOffset( + kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()); + // temp = temp[index_in_cache] + __ LoadFromOffset( + kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex())); + // LR = temp[offset_of_quick_compiled_code] + __ LoadFromOffset(kLoadWord, LR, temp, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArmWordSize).Int32Value()); + // LR() + __ blx(LR); + } else { + __ bl(GetFrameEntryLabel()); + } } DCHECK(!IsLeafMethod()); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index dada4ce5bd..946ffc8ea8 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1908,7 +1908,7 @@ void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) { locations->AddTemp(LocationFrom(x0)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -1968,13 +1968,9 @@ void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { } void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetArena()); if (intrinsic.TryDispatch(invoke)) { @@ -2006,29 +2002,39 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset())); - // temp = temp[index_in_cache]; - __ Ldr(temp, HeapOperand(temp, index_in_cache)); - // lr = temp->entry_point_from_quick_compiled_code_; + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ Ldr(temp, HeapOperand(tr, invoke->GetStringInitOffset())); + // LR = temp->entry_point_from_quick_compiled_code_; __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( kArm64WordSize))); - // lr(); + // lr() __ Blr(lr); } else { - __ Bl(&frame_entry_label_); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ Ldr(temp, HeapOperand(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset())); + // temp = temp[index_in_cache]; + __ Ldr(temp, HeapOperand(temp, index_in_cache)); + // lr = temp->entry_point_from_quick_compiled_code_; + __ Ldr(lr, HeapOperand(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kArm64WordSize))); + // lr(); + __ Blr(lr); + } else { + __ Bl(&frame_entry_label_); + } } DCHECK(!IsLeafMethod()); } void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 04999bedb0..7df4b53cbe 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1194,13 +1194,9 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { } void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderX86 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -1220,9 +1216,9 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86* codegen) } void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; @@ -1243,7 +1239,7 @@ void LocationsBuilderX86::HandleInvoke(HInvoke* invoke) { locations->AddTemp(Location::RegisterLocation(EAX)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -3114,18 +3110,27 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, // 3) app -> app // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ fs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset())); // (temp + offset_of_quick_compiled_code)() __ call(Address( temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); } else { - __ call(GetFrameEntryLabel()); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + // (temp + offset_of_quick_compiled_code)() + __ call(Address(temp, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); + } else { + __ call(GetFrameEntryLabel()); + } } DCHECK(!IsLeafMethod()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 5ce932928b..37b00c8d52 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -366,18 +366,26 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo // // Currently we implement the app -> app logic, which looks up in the resolve cache. - // temp = method; - LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + if (invoke->IsStringInit()) { + // temp = thread->string_init_entrypoint + __ gs()->movl(temp, Address::Absolute(invoke->GetStringInitOffset())); // (temp + offset_of_quick_compiled_code)() __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( kX86_64WordSize).SizeValue())); } else { - __ call(&frame_entry_label_); + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + // (temp + offset_of_quick_compiled_code)() + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( + kX86_64WordSize).SizeValue())); + } else { + __ call(&frame_entry_label_); + } } DCHECK(!IsLeafMethod()); @@ -1289,13 +1297,9 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type } void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation, but this step is not - // run in baseline. So we remove them manually here if we find them. - // TODO: Instead of this local workaround, address this properly. - if (invoke->IsStaticWithExplicitClinitCheck()) { - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); - } + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); IntrinsicLocationsBuilderX86_64 intrinsic(codegen_); if (intrinsic.TryDispatch(invoke)) { @@ -1315,9 +1319,9 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86_64* codeg } void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - // Explicit clinit checks triggered by static invokes must have been - // pruned by art::PrepareForRegisterAllocation. - DCHECK(!invoke->IsStaticWithExplicitClinitCheck()); + // When we do not run baseline, explicit clinit checks triggered by static + // invokes must have been pruned by art::PrepareForRegisterAllocation. + DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); if (TryGenerateIntrinsicCode(invoke, codegen_)) { return; @@ -1335,7 +1339,7 @@ void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) { locations->AddTemp(Location::RegisterLocation(RDI)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 6cdc82262c..e3fd5d7ec1 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -37,7 +37,7 @@ static constexpr FloatRegister kParameterFloatRegisters[] = static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); static constexpr size_t kParameterFloatRegistersLength = arraysize(kParameterFloatRegisters); -static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; +static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX, RCX }; static constexpr size_t kRuntimeParameterCoreRegistersLength = arraysize(kRuntimeParameterCoreRegisters); static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1 }; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2df7c166d8..e79d4f4bdc 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -377,15 +377,42 @@ void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) { return; } - if ((input_cst != nullptr) && input_cst->IsMinusOne() && - (Primitive::IsFloatingPointType(type) || Primitive::IsIntOrLongType(type))) { + if ((input_cst != nullptr) && input_cst->IsMinusOne()) { // Replace code looking like // DIV dst, src, -1 // with // NEG dst, src instruction->GetBlock()->ReplaceAndRemoveInstructionWith( - instruction, (new (GetGraph()->GetArena()) HNeg(type, input_other))); + instruction, new (GetGraph()->GetArena()) HNeg(type, input_other)); RecordSimplification(); + return; + } + + if ((input_cst != nullptr) && Primitive::IsFloatingPointType(type)) { + // Try replacing code looking like + // DIV dst, src, constant + // with + // MUL dst, src, 1 / constant + HConstant* reciprocal = nullptr; + if (type == Primitive::Primitive::kPrimDouble) { + double value = input_cst->AsDoubleConstant()->GetValue(); + if (CanDivideByReciprocalMultiplyDouble(bit_cast<int64_t, double>(value))) { + reciprocal = GetGraph()->GetDoubleConstant(1.0 / value); + } + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + float value = input_cst->AsFloatConstant()->GetValue(); + if (CanDivideByReciprocalMultiplyFloat(bit_cast<int32_t, float>(value))) { + reciprocal = GetGraph()->GetFloatConstant(1.0f / value); + } + } + + if (reciprocal != nullptr) { + instruction->GetBlock()->ReplaceAndRemoveInstructionWith( + instruction, new (GetGraph()->GetArena()) HMul(type, input_other, reciprocal)); + RecordSimplification(); + return; + } } } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 20aa45f197..5d3db5c6f2 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -186,6 +186,8 @@ static Intrinsics GetIntrinsic(InlineMethod method) { return Intrinsics::kStringCharAt; case kIntrinsicCompareTo: return Intrinsics::kStringCompareTo; + case kIntrinsicGetCharsNoCheck: + return Intrinsics::kStringGetCharsNoCheck; case kIntrinsicIsEmptyOrLength: // The inliner can handle these two cases - and this is the preferred approach // since after inlining the call is no longer visible (as opposed to waiting @@ -194,6 +196,12 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicIndexOf: return ((method.d.data & kIntrinsicFlagBase0) == 0) ? Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; + case kIntrinsicNewStringFromBytes: + return Intrinsics::kStringNewStringFromBytes; + case kIntrinsicNewStringFromChars: + return Intrinsics::kStringNewStringFromChars; + case kIntrinsicNewStringFromString: + return Intrinsics::kStringNewStringFromString; case kIntrinsicCas: switch (GetType(method.d.data, false)) { @@ -280,6 +288,11 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kInlineOpIPut: return Intrinsics::kNone; + // String init cases, not intrinsics. + + case kInlineStringInit: + return Intrinsics::kNone; + // No default case to make the compiler warn on missing cases. } return Intrinsics::kNone; diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index abdf04ebb1..e3fa272d22 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -48,7 +48,7 @@ static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGener DCHECK_NE(type, Primitive::kPrimVoid); - if (Primitive::IsIntegralType(type)) { + if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { if (type == Primitive::kPrimLong) { Register trg_reg_lo = trg.AsRegisterPairLow<Register>(); Register trg_reg_hi = trg.AsRegisterPairHigh<Register>(); @@ -78,7 +78,7 @@ static void MoveFromReturnRegister(Location trg, Primitive::Type type, CodeGener } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -90,7 +90,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorA // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -810,10 +810,6 @@ void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { const MemberOffset value_offset = mirror::String::ValueOffset(); // Location of count const MemberOffset count_offset = mirror::String::CountOffset(); - // Starting offset within data array - const MemberOffset offset_offset = mirror::String::OffsetOffset(); - // Start of char data with array_ - const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t)); Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer. Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character. @@ -835,15 +831,10 @@ void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { __ cmp(idx, ShifterOperand(temp)); __ b(slow_path->GetEntryLabel(), CS); - // Index computation. - __ ldr(temp, Address(obj, offset_offset.Int32Value())); // temp := str.offset. - __ ldr(array_temp, Address(obj, value_offset.Int32Value())); // array_temp := str.offset. - __ add(temp, temp, ShifterOperand(idx)); - DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting. - __ add(temp, temp, ShifterOperand(data_offset.Int32Value() / 2)); + __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value. // Load the value. - __ ldrh(out, Address(array_temp, temp, LSL, 1)); // out := array_temp[temp]. + __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx]. __ Bind(slow_path->GetExitLabel()); } @@ -878,6 +869,81 @@ void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = locations->InAt(0).AsRegister<Register>(); + __ cmp(byte_array, ShifterOperand(0)); + SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel(), EQ); + + __ LoadFromOffset( + kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ blx(LR); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + + __ LoadFromOffset( + kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ blx(LR); +} + +void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = locations->InAt(0).AsRegister<Register>(); + __ cmp(string_to_copy, ShifterOperand(0)); + SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel(), EQ); + + __ LoadFromOffset(kLoadWord, + LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ blx(LR); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -907,6 +973,7 @@ UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) } // namespace arm } // namespace art diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 7a753b2da9..d71b49e6f1 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -75,7 +75,7 @@ static void MoveFromReturnRegister(Location trg, DCHECK_NE(type, Primitive::kPrimVoid); - if (Primitive::IsIntegralType(type)) { + if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { Register trg_reg = RegisterFrom(trg, type); Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type); __ Mov(trg_reg, res_reg, kDiscardForSameWReg); @@ -87,7 +87,7 @@ static void MoveFromReturnRegister(Location trg, } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorARM64* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -99,7 +99,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorA // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -953,10 +953,6 @@ void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { const MemberOffset value_offset = mirror::String::ValueOffset(); // Location of count const MemberOffset count_offset = mirror::String::CountOffset(); - // Starting offset within data array - const MemberOffset offset_offset = mirror::String::OffsetOffset(); - // Start of char data with array_ - const MemberOffset data_offset = mirror::Array::DataOffset(sizeof(uint16_t)); Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer. Register idx = WRegisterFrom(locations->InAt(1)); // Index of character. @@ -979,21 +975,15 @@ void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { __ Cmp(idx, temp); __ B(hs, slow_path->GetEntryLabel()); - // Index computation. - __ Ldr(temp, HeapOperand(obj, offset_offset)); // temp := str.offset. - __ Ldr(array_temp, HeapOperand(obj, value_offset)); // array_temp := str.offset. - __ Add(temp, temp, idx); - DCHECK_EQ(data_offset.Int32Value() % 2, 0); // We'll compensate by shifting. - __ Add(temp, temp, Operand(data_offset.Int32Value() / 2)); + __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value. // Load the value. - __ Ldrh(out, MemOperand(array_temp.X(), temp, UXTW, 1)); // out := array_temp[temp]. + __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx]. __ Bind(slow_path->GetExitLabel()); } void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) { - // The inputs plus one temp. LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, kIntrinsified); @@ -1022,6 +1012,84 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, LocationFrom(calling_convention.GetRegisterAt(3))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = WRegisterFrom(locations->InAt(0)); + __ Cmp(byte_array, 0); + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + __ B(eq, slow_path->GetEntryLabel()); + + __ Ldr(lr, + MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value())); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Blr(lr); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + + __ Ldr(lr, + MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value())); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Blr(lr); +} + +void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) { + // The inputs plus one temp. + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = WRegisterFrom(locations->InAt(0)); + __ Cmp(string_to_copy, 0); + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + __ B(eq, slow_path->GetEntryLabel()); + + __ Ldr(lr, + MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value())); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Blr(lr); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1034,6 +1102,7 @@ UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 10f6e1d6c7..2c9248f52c 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -60,8 +60,12 @@ V(MemoryPokeShortNative, kStatic) \ V(StringCharAt, kDirect) \ V(StringCompareTo, kDirect) \ + V(StringGetCharsNoCheck, kDirect) \ V(StringIndexOf, kDirect) \ V(StringIndexOfAfter, kDirect) \ + V(StringNewStringFromBytes, kStatic) \ + V(StringNewStringFromChars, kStatic) \ + V(StringNewStringFromString, kStatic) \ V(UnsafeCASInt, kDirect) \ V(UnsafeCASLong, kDirect) \ V(UnsafeCASObject, kDirect) \ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 7275edb695..18fb3c4d43 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -112,7 +112,7 @@ static void MoveFromReturnRegister(Location target, } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -124,7 +124,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -910,23 +910,18 @@ void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array - const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_ - const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); Register obj = locations->InAt(0).AsRegister<Register>(); Register idx = locations->InAt(1).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); - Location temp_loc = locations->GetTemp(0); - Register temp = temp_loc.AsRegister<Register>(); // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth // the cost. // TODO: For simplicity, the index parameter is requested in a register, so different from Quick // we will not optimize the code for constants (which would save a register). - SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke, temp); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); codegen_->AddSlowPath(slow_path); X86Assembler* assembler = GetAssembler(); @@ -935,12 +930,8 @@ void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { codegen_->MaybeRecordImplicitNullCheck(invoke); __ j(kAboveEqual, slow_path->GetEntryLabel()); - // Get the actual element. - __ movl(temp, idx); // temp := idx. - __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx. - __ movl(out, Address(obj, value_offset)); // obj := obj.array. - // out = out[2*temp]. - __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset)); + // out = out[2*idx]. + __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); __ Bind(slow_path->GetExitLabel()); } @@ -976,6 +967,81 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderX86::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + locations->SetOut(Location::RegisterLocation(EAX)); + // Needs to be EAX for the invoke. + locations->AddTemp(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = locations->InAt(0).AsRegister<Register>(); + __ testl(byte_array, byte_array); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromBytes))); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderX86::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromChars))); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void IntrinsicLocationsBuilderX86::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(Location::RegisterLocation(EAX)); + // Needs to be EAX for the invoke. + locations->AddTemp(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = locations->InAt(0).AsRegister<Register>(); + __ testl(string_to_copy, string_to_copy); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromString))); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + static void GenPeek(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) { Register address = locations->InAt(0).AsRegisterPairLow<Register>(); Location out_loc = locations->Out(); @@ -1536,6 +1602,7 @@ void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) } UNIMPLEMENTED_INTRINSIC(MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 35daaf60bb..db7b58bc66 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -104,7 +104,7 @@ static void MoveFromReturnRegister(Location trg, } static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86_64* codegen) { - if (invoke->InputCount() == 0) { + if (invoke->GetNumberOfArguments() == 0) { // No argument to move. return; } @@ -116,7 +116,7 @@ static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX // a parallel move resolver. HParallelMove parallel_move(arena); - for (size_t i = 0; i < invoke->InputCount(); i++) { + for (size_t i = 0; i < invoke->GetNumberOfArguments(); i++) { HInstruction* input = invoke->InputAt(i); Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); Location actual_loc = locations->InAt(i); @@ -824,16 +824,10 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); // Location of count const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - // Starting offset within data array - const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value(); - // Start of char data with array_ - const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); - Location temp_loc = locations->GetTemp(0); - CpuRegister temp = temp_loc.AsRegister<CpuRegister>(); // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth // the cost. @@ -849,12 +843,8 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { codegen_->MaybeRecordImplicitNullCheck(invoke); __ j(kAboveEqual, slow_path->GetEntryLabel()); - // Get the actual element. - __ movl(temp, idx); // temp := idx. - __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx. - __ movl(out, Address(obj, value_offset)); // obj := obj.array. - // out = out[2*temp]. - __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset)); + // out = out[2*idx]. + __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); __ Bind(slow_path->GetExitLabel()); } @@ -887,6 +877,78 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromBytes(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + CpuRegister byte_array = locations->InAt(0).AsRegister<CpuRegister>(); + __ testl(byte_array, byte_array); + SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromBytes), true)); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromChars(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromChars), true)); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromString(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + CpuRegister string_to_copy = locations->InAt(0).AsRegister<CpuRegister>(); + __ testl(string_to_copy, string_to_copy); + SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromString), true)); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) { CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); // == address, here for clarity. @@ -1390,6 +1452,7 @@ void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UN void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index cc76d9ca85..f64086e607 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -561,6 +561,13 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { predecessors_.Put(1, temp); } + void SwapSuccessors() { + DCHECK_EQ(successors_.Size(), 2u); + HBasicBlock* temp = successors_.Get(0); + successors_.Put(0, successors_.Get(1)); + successors_.Put(1, temp); + } + size_t GetPredecessorIndexOf(HBasicBlock* predecessor) { for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) { if (predecessors_.Get(i) == predecessor) { @@ -2223,6 +2230,12 @@ class HInvoke : public HInstruction { SetRawInputAt(index, argument); } + // Return the number of arguments. This number can be lower than + // the number of inputs returned by InputCount(), as some invoke + // instructions (e.g. HInvokeStaticOrDirect) can have non-argument + // inputs at the end of their list of inputs. + uint32_t GetNumberOfArguments() const { return number_of_arguments_; } + Primitive::Type GetType() const OVERRIDE { return return_type_; } uint32_t GetDexPc() const { return dex_pc_; } @@ -2242,16 +2255,19 @@ class HInvoke : public HInstruction { protected: HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, + uint32_t number_of_other_inputs, Primitive::Type return_type, uint32_t dex_pc, uint32_t dex_method_index) : HInstruction(SideEffects::All()), + number_of_arguments_(number_of_arguments), inputs_(arena, number_of_arguments), return_type_(return_type), dex_pc_(dex_pc), dex_method_index_(dex_method_index), intrinsic_(Intrinsics::kNone) { - inputs_.SetSize(number_of_arguments); + uint32_t number_of_inputs = number_of_arguments + number_of_other_inputs; + inputs_.SetSize(number_of_inputs); } const HUserRecord<HInstruction*> InputRecordAt(size_t i) const OVERRIDE { return inputs_.Get(i); } @@ -2259,6 +2275,7 @@ class HInvoke : public HInstruction { inputs_.Put(index, input); } + uint32_t number_of_arguments_; GrowableArray<HUserRecord<HInstruction*> > inputs_; const Primitive::Type return_type_; const uint32_t dex_pc_; @@ -2285,14 +2302,21 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t dex_pc, uint32_t dex_method_index, bool is_recursive, + int32_t string_init_offset, InvokeType original_invoke_type, InvokeType invoke_type, ClinitCheckRequirement clinit_check_requirement) - : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), + : HInvoke(arena, + number_of_arguments, + clinit_check_requirement == ClinitCheckRequirement::kExplicit ? 1u : 0u, + return_type, + dex_pc, + dex_method_index), original_invoke_type_(original_invoke_type), invoke_type_(invoke_type), is_recursive_(is_recursive), - clinit_check_requirement_(clinit_check_requirement) {} + clinit_check_requirement_(clinit_check_requirement), + string_init_offset_(string_init_offset) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { UNUSED(obj); @@ -2305,21 +2329,24 @@ class HInvokeStaticOrDirect : public HInvoke { InvokeType GetInvokeType() const { return invoke_type_; } bool IsRecursive() const { return is_recursive_; } bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); } + bool IsStringInit() const { return string_init_offset_ != 0; } + int32_t GetStringInitOffset() const { return string_init_offset_; } // Is this instruction a call to a static method? bool IsStatic() const { return GetInvokeType() == kStatic; } - // Remove the art::HClinitCheck or art::HLoadClass instruction as - // last input (only relevant for static calls with explicit clinit - // check). - void RemoveClinitCheckOrLoadClassAsLastInput() { + // Remove the art::HLoadClass instruction set as last input by + // art::PrepareForRegisterAllocation::VisitClinitCheck in lieu of + // the initial art::HClinitCheck instruction (only relevant for + // static calls with explicit clinit check). + void RemoveLoadClassAsLastInput() { DCHECK(IsStaticWithExplicitClinitCheck()); size_t last_input_index = InputCount() - 1; HInstruction* last_input = InputAt(last_input_index); DCHECK(last_input != nullptr); - DCHECK(last_input->IsClinitCheck() || last_input->IsLoadClass()) << last_input->DebugName(); + DCHECK(last_input->IsLoadClass()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); inputs_.DeleteAt(last_input_index); clinit_check_requirement_ = ClinitCheckRequirement::kImplicit; @@ -2360,6 +2387,9 @@ class HInvokeStaticOrDirect : public HInvoke { const InvokeType invoke_type_; const bool is_recursive_; ClinitCheckRequirement clinit_check_requirement_; + // Thread entrypoint offset for string init method if this is a string init invoke. + // Note that there are multiple string init methods, each having its own offset. + int32_t string_init_offset_; DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect); }; @@ -2372,7 +2402,7 @@ class HInvokeVirtual : public HInvoke { uint32_t dex_pc, uint32_t dex_method_index, uint32_t vtable_index) - : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), + : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index), vtable_index_(vtable_index) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { @@ -2398,7 +2428,7 @@ class HInvokeInterface : public HInvoke { uint32_t dex_pc, uint32_t dex_method_index, uint32_t imt_index) - : HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index), + : HInvoke(arena, number_of_arguments, 0u, return_type, dex_pc, dex_method_index), imt_index_(imt_index) {} bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index fa6b3c292c..78d11857c3 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -91,7 +91,7 @@ void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDire // previously) by the graph builder during the creation of the // static invoke instruction, but is no longer required at this // stage (i.e., after inlining has been performed). - invoke->RemoveClinitCheckOrLoadClassAsLastInput(); + invoke->RemoveLoadClassAsLastInput(); // If the load class instruction is no longer used, remove it from // the graph. diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 0fdf051957..a8d006f104 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -1455,6 +1455,7 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { : Location::StackSlot(interval->GetParent()->GetSpillSlot())); } UsePosition* use = current->GetFirstUse(); + UsePosition* env_use = current->GetFirstEnvironmentUse(); // Walk over all siblings, updating locations of use positions, and // connecting them when they are adjacent. @@ -1466,32 +1467,39 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { LiveRange* range = current->GetFirstRange(); while (range != nullptr) { - while (use != nullptr && use->GetPosition() < range->GetStart()) { - DCHECK(use->GetIsEnvironment()); - use = use->GetNext(); - } + DCHECK(use == nullptr || use->GetPosition() >= range->GetStart()); while (use != nullptr && use->GetPosition() <= range->GetEnd()) { + DCHECK(!use->GetIsEnvironment()); DCHECK(current->CoversSlow(use->GetPosition()) || (use->GetPosition() == range->GetEnd())); LocationSummary* locations = use->GetUser()->GetLocations(); - if (use->GetIsEnvironment()) { - locations->SetEnvironmentAt(use->GetInputIndex(), source); - } else { - Location expected_location = locations->InAt(use->GetInputIndex()); - // The expected (actual) location may be invalid in case the input is unused. Currently - // this only happens for intrinsics. - if (expected_location.IsValid()) { - if (expected_location.IsUnallocated()) { - locations->SetInAt(use->GetInputIndex(), source); - } else if (!expected_location.IsConstant()) { - AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location); - } - } else { - DCHECK(use->GetUser()->IsInvoke()); - DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone); + Location expected_location = locations->InAt(use->GetInputIndex()); + // The expected (actual) location may be invalid in case the input is unused. Currently + // this only happens for intrinsics. + if (expected_location.IsValid()) { + if (expected_location.IsUnallocated()) { + locations->SetInAt(use->GetInputIndex(), source); + } else if (!expected_location.IsConstant()) { + AddInputMoveFor(interval->GetDefinedBy(), use->GetUser(), source, expected_location); } + } else { + DCHECK(use->GetUser()->IsInvoke()); + DCHECK(use->GetUser()->AsInvoke()->GetIntrinsic() != Intrinsics::kNone); } use = use->GetNext(); } + + // Walk over the environment uses, and update their locations. + while (env_use != nullptr && env_use->GetPosition() < range->GetStart()) { + env_use = env_use->GetNext(); + } + + while (env_use != nullptr && env_use->GetPosition() <= range->GetEnd()) { + DCHECK(current->CoversSlow(env_use->GetPosition()) || (env_use->GetPosition() == range->GetEnd())); + LocationSummary* locations = env_use->GetUser()->GetLocations(); + locations->SetEnvironmentAt(env_use->GetInputIndex(), source); + env_use = env_use->GetNext(); + } + range = range->GetNext(); } @@ -1553,14 +1561,7 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { current = next_sibling; } while (current != nullptr); - if (kIsDebugBuild) { - // Following uses can only be environment uses. The location for - // these environments will be none. - while (use != nullptr) { - DCHECK(use->GetIsEnvironment()); - use = use->GetNext(); - } - } + DCHECK(use == nullptr); } void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index ea0e7c3712..b674f746b6 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -341,7 +341,7 @@ int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); - if (use_position >= start && !use->GetIsEnvironment()) { + if (use_position >= start) { HInstruction* user = use->GetUser(); size_t input_index = use->GetInputIndex(); if (user->IsPhi()) { diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 97254edb5e..b95276afd7 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -219,6 +219,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { void AddTempUse(HInstruction* instruction, size_t temp_index) { DCHECK(IsTemp()); DCHECK(first_use_ == nullptr) << "A temporary can only have one user"; + DCHECK(first_env_use_ == nullptr) << "A temporary cannot have environment user"; size_t position = instruction->GetLifetimePosition(); first_use_ = new (allocator_) UsePosition( instruction, temp_index, /* is_environment */ false, position, first_use_); @@ -265,8 +266,13 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { return; } - first_use_ = new (allocator_) UsePosition( - instruction, input_index, is_environment, position, first_use_); + if (is_environment) { + first_env_use_ = new (allocator_) UsePosition( + instruction, input_index, is_environment, position, first_env_use_); + } else { + first_use_ = new (allocator_) UsePosition( + instruction, input_index, is_environment, position, first_use_); + } if (is_environment && !keep_alive) { // If this environment use does not keep the instruction live, it does not @@ -477,7 +483,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { size_t use_position = use->GetPosition(); - if (use_position > position && !use->GetIsEnvironment()) { + if (use_position > position) { Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); if (location.IsUnallocated() && (location.GetPolicy() == Location::kRequiresRegister @@ -508,12 +514,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { UsePosition* use = first_use_; size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { - if (!use->GetIsEnvironment()) { - Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); - size_t use_position = use->GetPosition(); - if (use_position > position && location.IsValid()) { - return use_position; - } + Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); + size_t use_position = use->GetPosition(); + if (use_position > position && location.IsValid()) { + return use_position; } use = use->GetNext(); } @@ -524,6 +528,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { return first_use_; } + UsePosition* GetFirstEnvironmentUse() const { + return first_env_use_; + } + Primitive::Type GetType() const { return type_; } @@ -577,6 +585,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { new_interval->parent_ = parent_; new_interval->first_use_ = first_use_; + new_interval->first_env_use_ = first_env_use_; LiveRange* current = first_range_; LiveRange* previous = nullptr; // Iterate over the ranges, and either find a range that covers this position, or @@ -655,10 +664,18 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { stream << " "; } while ((use = use->GetNext()) != nullptr); } + stream << "}, {"; + use = first_env_use_; + if (use != nullptr) { + do { + use->Dump(stream); + stream << " "; + } while ((use = use->GetNext()) != nullptr); + } stream << "}"; stream << " is_fixed: " << is_fixed_ << ", is_split: " << IsSplit(); - stream << " is_high: " << IsHighInterval(); stream << " is_low: " << IsLowInterval(); + stream << " is_high: " << IsHighInterval(); } LiveInterval* GetNextSibling() const { return next_sibling_; } @@ -754,6 +771,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { if (first_use_ != nullptr) { high_or_low_interval_->first_use_ = first_use_->Dup(allocator_); } + + if (first_env_use_ != nullptr) { + high_or_low_interval_->first_env_use_ = first_env_use_->Dup(allocator_); + } } // Returns whether an interval, when it is non-split, is using @@ -851,6 +872,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { first_safepoint_(nullptr), last_safepoint_(nullptr), first_use_(nullptr), + first_env_use_(nullptr), type_(type), next_sibling_(nullptr), parent_(this), @@ -905,6 +927,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { // Uses of this interval. Note that this linked list is shared amongst siblings. UsePosition* first_use_; + UsePosition* first_env_use_; // The instruction type this interval corresponds to. const Primitive::Type type_; |