diff options
24 files changed, 754 insertions, 265 deletions
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index d89cdbabf8..9a45379a05 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -52,10 +52,10 @@ void CommonCompilerTest::MakeExecutable(ArtMethod* method) { compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method->GetDexMethodIndex())); } - if (compiled_method != nullptr) { + // If the code size is 0 it means the method was skipped due to profile guided compilation. + if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) { ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); uint32_t code_size = code.size(); - CHECK_NE(0u, code_size); ArrayRef<const uint8_t> vmap_table = compiled_method->GetVmapTable(); uint32_t vmap_table_offset = vmap_table.empty() ? 0u : sizeof(OatQuickMethodHeader) + vmap_table.size(); diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc index bfd485d126..53601033da 100644 --- a/compiler/dex/dex_to_dex_decompiler.cc +++ b/compiler/dex/dex_to_dex_decompiler.cc @@ -20,7 +20,7 @@ #include "base/mutex.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" -#include "optimizing/bytecode_utils.h" +#include "bytecode_utils.h" namespace art { namespace optimizer { diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 97954f3c29..562f97b3ae 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -240,9 +240,8 @@ class CompilerDriverProfileTest : public CompilerDriverTest { ProfileCompilationInfo info; for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { - std::string key = ProfileCompilationInfo::GetProfileDexFileKey(dex_file->GetLocation()); - profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 1); - profile_info_.AddMethodIndex(key, dex_file->GetLocationChecksum(), 2); + profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 1); + profile_info_.AddMethodIndex(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 2); } return &profile_info_; } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 791e63265e..51dd898a81 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1899,9 +1899,9 @@ void LocationsBuilderMIPS::VisitArrayGet(HArrayGet* instruction) { } } -auto InstructionCodeGeneratorMIPS::GetImplicitNullChecker(HInstruction* instruction) { - auto null_checker = [this, instruction]() { - this->codegen_->MaybeRecordImplicitNullCheck(instruction); +static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS* codegen) { + auto null_checker = [codegen, instruction]() { + codegen->MaybeRecordImplicitNullCheck(instruction); }; return null_checker; } @@ -1911,7 +1911,7 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { Register obj = locations->InAt(0).AsRegister<Register>(); Location index = locations->InAt(1); uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); - auto null_checker = GetImplicitNullChecker(instruction); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); Primitive::Type type = instruction->GetType(); const bool maybe_compressed_char_at = mirror::kUseStringCompression && @@ -2148,7 +2148,7 @@ void InstructionCodeGeneratorMIPS::VisitArraySet(HArraySet* instruction) { bool needs_runtime_call = locations->WillCall(); bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); - auto null_checker = GetImplicitNullChecker(instruction); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); Register base_reg = index.IsConstant() ? obj : TMP; switch (value_type) { @@ -4923,7 +4923,7 @@ void InstructionCodeGeneratorMIPS::HandleFieldGet(HInstruction* instruction, LoadOperandType load_type = kLoadUnsignedByte; bool is_volatile = field_info.IsVolatile(); uint32_t offset = field_info.GetFieldOffset().Uint32Value(); - auto null_checker = GetImplicitNullChecker(instruction); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); switch (type) { case Primitive::kPrimBoolean: @@ -5052,7 +5052,7 @@ void InstructionCodeGeneratorMIPS::HandleFieldSet(HInstruction* instruction, bool is_volatile = field_info.IsVolatile(); uint32_t offset = field_info.GetFieldOffset().Uint32Value(); bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1)); - auto null_checker = GetImplicitNullChecker(instruction); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); switch (type) { case Primitive::kPrimBoolean: diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 47eba50248..0ccd80ab93 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -297,7 +297,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction); void GenerateDivRemIntegral(HBinaryOperation* instruction); void HandleGoto(HInstruction* got, HBasicBlock* successor); - auto GetImplicitNullChecker(HInstruction* instruction); void GenPackedSwitchWithCompares(Register value_reg, int32_t lower_bound, uint32_t num_entries, diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 817854b507..d3f3598e2a 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1483,11 +1483,19 @@ void LocationsBuilderMIPS64::VisitArrayGet(HArrayGet* instruction) { } } +static auto GetImplicitNullChecker(HInstruction* instruction, CodeGeneratorMIPS64* codegen) { + auto null_checker = [codegen, instruction]() { + codegen->MaybeRecordImplicitNullCheck(instruction); + }; + return null_checker; +} + void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); Location index = locations->InAt(1); uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); Primitive::Type type = instruction->GetType(); const bool maybe_compressed_char_at = mirror::kUseStringCompression && @@ -1498,10 +1506,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; - __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset); + __ LoadFromOffset(kLoadUnsignedByte, out, obj, offset, null_checker); } else { __ Daddu(TMP, obj, index.AsRegister<GpuRegister>()); - __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset); + __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset, null_checker); } break; } @@ -1511,10 +1519,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; - __ LoadFromOffset(kLoadSignedByte, out, obj, offset); + __ LoadFromOffset(kLoadSignedByte, out, obj, offset, null_checker); } else { __ Daddu(TMP, obj, index.AsRegister<GpuRegister>()); - __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset); + __ LoadFromOffset(kLoadSignedByte, out, TMP, data_offset, null_checker); } break; } @@ -1524,11 +1532,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; - __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset); + __ LoadFromOffset(kLoadSignedHalfword, out, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2); __ Daddu(TMP, obj, TMP); - __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset); + __ LoadFromOffset(kLoadSignedHalfword, out, TMP, data_offset, null_checker); } break; } @@ -1537,8 +1545,7 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (maybe_compressed_char_at) { uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); - __ LoadFromOffset(kLoadWord, TMP, obj, count_offset); - codegen_->MaybeRecordImplicitNullCheck(instruction); + __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker); __ Dext(TMP, TMP, 0, 1); static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); @@ -1563,7 +1570,8 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, - data_offset + (const_index << TIMES_2)); + data_offset + (const_index << TIMES_2), + null_checker); } } else { GpuRegister index_reg = index.AsRegister<GpuRegister>(); @@ -1581,7 +1589,7 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } else { __ Dsll(TMP, index_reg, TIMES_2); __ Daddu(TMP, obj, TMP); - __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset); + __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker); } } break; @@ -1595,11 +1603,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; - __ LoadFromOffset(load_type, out, obj, offset); + __ LoadFromOffset(load_type, out, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); __ Daddu(TMP, obj, TMP); - __ LoadFromOffset(load_type, out, TMP, data_offset); + __ LoadFromOffset(load_type, out, TMP, data_offset, null_checker); } break; } @@ -1609,11 +1617,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; - __ LoadFromOffset(kLoadDoubleword, out, obj, offset); + __ LoadFromOffset(kLoadDoubleword, out, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); __ Daddu(TMP, obj, TMP); - __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset); + __ LoadFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker); } break; } @@ -1623,11 +1631,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; - __ LoadFpuFromOffset(kLoadWord, out, obj, offset); + __ LoadFpuFromOffset(kLoadWord, out, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); __ Daddu(TMP, obj, TMP); - __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset); + __ LoadFpuFromOffset(kLoadWord, out, TMP, data_offset, null_checker); } break; } @@ -1637,11 +1645,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; - __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset); + __ LoadFpuFromOffset(kLoadDoubleword, out, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); __ Daddu(TMP, obj, TMP); - __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset); + __ LoadFpuFromOffset(kLoadDoubleword, out, TMP, data_offset, null_checker); } break; } @@ -1650,6 +1658,7 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); } + if (!maybe_compressed_char_at) { codegen_->MaybeRecordImplicitNullCheck(instruction); } @@ -1708,6 +1717,7 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { bool needs_runtime_call = locations->WillCall(); bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue()); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); switch (value_type) { case Primitive::kPrimBoolean: @@ -1717,10 +1727,10 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + data_offset; - __ StoreToOffset(kStoreByte, value, obj, offset); + __ StoreToOffset(kStoreByte, value, obj, offset, null_checker); } else { __ Daddu(TMP, obj, index.AsRegister<GpuRegister>()); - __ StoreToOffset(kStoreByte, value, TMP, data_offset); + __ StoreToOffset(kStoreByte, value, TMP, data_offset, null_checker); } break; } @@ -1732,11 +1742,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; - __ StoreToOffset(kStoreHalfword, value, obj, offset); + __ StoreToOffset(kStoreHalfword, value, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2); __ Daddu(TMP, obj, TMP); - __ StoreToOffset(kStoreHalfword, value, TMP, data_offset); + __ StoreToOffset(kStoreHalfword, value, TMP, data_offset, null_checker); } break; } @@ -1786,10 +1796,10 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { } __ PoisonHeapReference(AT, value); __ Sw(AT, base_reg, data_offset); + null_checker(); } else { - __ StoreToOffset(kStoreWord, value, base_reg, data_offset); + __ StoreToOffset(kStoreWord, value, base_reg, data_offset, null_checker); } - codegen_->MaybeRecordImplicitNullCheck(instruction); if (needs_write_barrier) { DCHECK_EQ(value_type, Primitive::kPrimNot); codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull()); @@ -1810,11 +1820,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; - __ StoreToOffset(kStoreDoubleword, value, obj, offset); + __ StoreToOffset(kStoreDoubleword, value, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); __ Daddu(TMP, obj, TMP); - __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset); + __ StoreToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker); } break; } @@ -1826,11 +1836,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; - __ StoreFpuToOffset(kStoreWord, value, obj, offset); + __ StoreFpuToOffset(kStoreWord, value, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_4); __ Daddu(TMP, obj, TMP); - __ StoreFpuToOffset(kStoreWord, value, TMP, data_offset); + __ StoreFpuToOffset(kStoreWord, value, TMP, data_offset, null_checker); } break; } @@ -1842,11 +1852,11 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; - __ StoreFpuToOffset(kStoreDoubleword, value, obj, offset); + __ StoreFpuToOffset(kStoreDoubleword, value, obj, offset, null_checker); } else { __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_8); __ Daddu(TMP, obj, TMP); - __ StoreFpuToOffset(kStoreDoubleword, value, TMP, data_offset); + __ StoreFpuToOffset(kStoreDoubleword, value, TMP, data_offset, null_checker); } break; } @@ -1855,11 +1865,6 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); } - - // Ints and objects are handled in the switch. - if (value_type != Primitive::kPrimInt && value_type != Primitive::kPrimNot) { - codegen_->MaybeRecordImplicitNullCheck(instruction); - } } void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { @@ -3128,6 +3133,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction, GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); LoadOperandType load_type = kLoadUnsignedByte; uint32_t offset = field_info.GetFieldOffset().Uint32Value(); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); + switch (type) { case Primitive::kPrimBoolean: load_type = kLoadUnsignedByte; @@ -3159,14 +3166,12 @@ void InstructionCodeGeneratorMIPS64::HandleFieldGet(HInstruction* instruction, if (!Primitive::IsFloatingPointType(type)) { DCHECK(locations->Out().IsRegister()); GpuRegister dst = locations->Out().AsRegister<GpuRegister>(); - __ LoadFromOffset(load_type, dst, obj, offset); + __ LoadFromOffset(load_type, dst, obj, offset, null_checker); } else { DCHECK(locations->Out().IsFpuRegister()); FpuRegister dst = locations->Out().AsFpuRegister<FpuRegister>(); - __ LoadFpuFromOffset(load_type, dst, obj, offset); + __ LoadFpuFromOffset(load_type, dst, obj, offset, null_checker); } - - codegen_->MaybeRecordImplicitNullCheck(instruction); // TODO: memory barrier? if (type == Primitive::kPrimNot) { @@ -3196,6 +3201,8 @@ void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction, StoreOperandType store_type = kStoreByte; uint32_t offset = field_info.GetFieldOffset().Uint32Value(); bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1)); + auto null_checker = GetImplicitNullChecker(instruction, codegen_); + switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: @@ -3227,17 +3234,16 @@ void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction, // need poisoning. DCHECK_EQ(type, Primitive::kPrimNot); __ PoisonHeapReference(TMP, src); - __ StoreToOffset(store_type, TMP, obj, offset); + __ StoreToOffset(store_type, TMP, obj, offset, null_checker); } else { - __ StoreToOffset(store_type, src, obj, offset); + __ StoreToOffset(store_type, src, obj, offset, null_checker); } } else { DCHECK(locations->InAt(1).IsFpuRegister()); FpuRegister src = locations->InAt(1).AsFpuRegister<FpuRegister>(); - __ StoreFpuToOffset(store_type, src, obj, offset); + __ StoreFpuToOffset(store_type, src, obj, offset, null_checker); } - codegen_->MaybeRecordImplicitNullCheck(instruction); // TODO: memory barriers? if (needs_write_barrier) { DCHECK(locations->InAt(1).IsRegister()); diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 47ddf2547a..1a5a23d10b 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -501,8 +501,10 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi bool is_float = false); private: + // This will be used as an argument for loads/stores + // when there is no need for implicit null checks. struct NoImplicitNullChecker { - void operator()() {} + void operator()() const {} }; public: diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc index 0f86f8843d..39eb5893d8 100644 --- a/compiler/utils/mips64/assembler_mips64.cc +++ b/compiler/utils/mips64/assembler_mips64.cc @@ -2020,80 +2020,18 @@ void Mips64Assembler::Bc1nez(FpuRegister ft, Mips64Label* label) { Bcond(label, kCondT, static_cast<GpuRegister>(ft), ZERO); } -void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base, +void Mips64Assembler::LoadFromOffset(LoadOperandType type, + GpuRegister reg, + GpuRegister base, int32_t offset) { - if (!IsInt<16>(offset) || - (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && - !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { - LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); - Daddu(AT, AT, base); - base = AT; - offset &= (kMips64DoublewordSize - 1); - } - - switch (type) { - case kLoadSignedByte: - Lb(reg, base, offset); - break; - case kLoadUnsignedByte: - Lbu(reg, base, offset); - break; - case kLoadSignedHalfword: - Lh(reg, base, offset); - break; - case kLoadUnsignedHalfword: - Lhu(reg, base, offset); - break; - case kLoadWord: - CHECK_ALIGNED(offset, kMips64WordSize); - Lw(reg, base, offset); - break; - case kLoadUnsignedWord: - CHECK_ALIGNED(offset, kMips64WordSize); - Lwu(reg, base, offset); - break; - case kLoadDoubleword: - if (!IsAligned<kMips64DoublewordSize>(offset)) { - CHECK_ALIGNED(offset, kMips64WordSize); - Lwu(reg, base, offset); - Lwu(TMP2, base, offset + kMips64WordSize); - Dinsu(reg, TMP2, 32, 32); - } else { - Ld(reg, base, offset); - } - break; - } + LoadFromOffset<>(type, reg, base, offset); } -void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base, +void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, + FpuRegister reg, + GpuRegister base, int32_t offset) { - if (!IsInt<16>(offset) || - (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && - !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { - LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); - Daddu(AT, AT, base); - base = AT; - offset &= (kMips64DoublewordSize - 1); - } - - switch (type) { - case kLoadWord: - CHECK_ALIGNED(offset, kMips64WordSize); - Lwc1(reg, base, offset); - break; - case kLoadDoubleword: - if (!IsAligned<kMips64DoublewordSize>(offset)) { - CHECK_ALIGNED(offset, kMips64WordSize); - Lwc1(reg, base, offset); - Lw(TMP2, base, offset + kMips64WordSize); - Mthc1(TMP2, reg); - } else { - Ldc1(reg, base, offset); - } - break; - default: - LOG(FATAL) << "UNREACHABLE"; - } + LoadFpuFromOffset<>(type, reg, base, offset); } void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, @@ -2123,72 +2061,18 @@ void Mips64Assembler::EmitLoad(ManagedRegister m_dst, GpuRegister src_register, } } -void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base, +void Mips64Assembler::StoreToOffset(StoreOperandType type, + GpuRegister reg, + GpuRegister base, int32_t offset) { - if (!IsInt<16>(offset) || - (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && - !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { - LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); - Daddu(AT, AT, base); - base = AT; - offset &= (kMips64DoublewordSize - 1); - } - - switch (type) { - case kStoreByte: - Sb(reg, base, offset); - break; - case kStoreHalfword: - Sh(reg, base, offset); - break; - case kStoreWord: - CHECK_ALIGNED(offset, kMips64WordSize); - Sw(reg, base, offset); - break; - case kStoreDoubleword: - if (!IsAligned<kMips64DoublewordSize>(offset)) { - CHECK_ALIGNED(offset, kMips64WordSize); - Sw(reg, base, offset); - Dsrl32(TMP2, reg, 0); - Sw(TMP2, base, offset + kMips64WordSize); - } else { - Sd(reg, base, offset); - } - break; - default: - LOG(FATAL) << "UNREACHABLE"; - } + StoreToOffset<>(type, reg, base, offset); } -void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base, +void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, + FpuRegister reg, + GpuRegister base, int32_t offset) { - if (!IsInt<16>(offset) || - (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && - !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { - LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); - Daddu(AT, AT, base); - base = AT; - offset &= (kMips64DoublewordSize - 1); - } - - switch (type) { - case kStoreWord: - CHECK_ALIGNED(offset, kMips64WordSize); - Swc1(reg, base, offset); - break; - case kStoreDoubleword: - if (!IsAligned<kMips64DoublewordSize>(offset)) { - CHECK_ALIGNED(offset, kMips64WordSize); - Mfhc1(TMP2, reg); - Swc1(reg, base, offset); - Sw(TMP2, base, offset + kMips64WordSize); - } else { - Sdc1(reg, base, offset); - } - break; - default: - LOG(FATAL) << "UNREACHABLE"; - } + StoreFpuToOffset<>(type, reg, base, offset); } static dwarf::Reg DWARFReg(GpuRegister reg) { diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h index ee15c6da80..8bbe862d19 100644 --- a/compiler/utils/mips64/assembler_mips64.h +++ b/compiler/utils/mips64/assembler_mips64.h @@ -772,6 +772,191 @@ class Mips64Assembler FINAL : public Assembler, public JNIMacroAssembler<Pointer void Bc1nez(FpuRegister ft, Mips64Label* label); void EmitLoad(ManagedRegister m_dst, GpuRegister src_register, int32_t src_offset, size_t size); + + private: + // This will be used as an argument for loads/stores + // when there is no need for implicit null checks. + struct NoImplicitNullChecker { + void operator()() const {} + }; + + public: + template <typename ImplicitNullChecker = NoImplicitNullChecker> + void LoadFromOffset(LoadOperandType type, + GpuRegister reg, + GpuRegister base, + int32_t offset, + ImplicitNullChecker null_checker = NoImplicitNullChecker()) { + if (!IsInt<16>(offset) || + (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && + !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { + LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); + Daddu(AT, AT, base); + base = AT; + offset &= (kMips64DoublewordSize - 1); + } + + switch (type) { + case kLoadSignedByte: + Lb(reg, base, offset); + break; + case kLoadUnsignedByte: + Lbu(reg, base, offset); + break; + case kLoadSignedHalfword: + Lh(reg, base, offset); + break; + case kLoadUnsignedHalfword: + Lhu(reg, base, offset); + break; + case kLoadWord: + CHECK_ALIGNED(offset, kMips64WordSize); + Lw(reg, base, offset); + break; + case kLoadUnsignedWord: + CHECK_ALIGNED(offset, kMips64WordSize); + Lwu(reg, base, offset); + break; + case kLoadDoubleword: + if (!IsAligned<kMips64DoublewordSize>(offset)) { + CHECK_ALIGNED(offset, kMips64WordSize); + Lwu(reg, base, offset); + null_checker(); + Lwu(TMP2, base, offset + kMips64WordSize); + Dinsu(reg, TMP2, 32, 32); + } else { + Ld(reg, base, offset); + null_checker(); + } + break; + } + if (type != kLoadDoubleword) { + null_checker(); + } + } + + template <typename ImplicitNullChecker = NoImplicitNullChecker> + void LoadFpuFromOffset(LoadOperandType type, + FpuRegister reg, + GpuRegister base, + int32_t offset, + ImplicitNullChecker null_checker = NoImplicitNullChecker()) { + if (!IsInt<16>(offset) || + (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && + !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { + LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); + Daddu(AT, AT, base); + base = AT; + offset &= (kMips64DoublewordSize - 1); + } + + switch (type) { + case kLoadWord: + CHECK_ALIGNED(offset, kMips64WordSize); + Lwc1(reg, base, offset); + null_checker(); + break; + case kLoadDoubleword: + if (!IsAligned<kMips64DoublewordSize>(offset)) { + CHECK_ALIGNED(offset, kMips64WordSize); + Lwc1(reg, base, offset); + null_checker(); + Lw(TMP2, base, offset + kMips64WordSize); + Mthc1(TMP2, reg); + } else { + Ldc1(reg, base, offset); + null_checker(); + } + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } + } + + template <typename ImplicitNullChecker = NoImplicitNullChecker> + void StoreToOffset(StoreOperandType type, + GpuRegister reg, + GpuRegister base, + int32_t offset, + ImplicitNullChecker null_checker = NoImplicitNullChecker()) { + if (!IsInt<16>(offset) || + (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && + !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { + LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); + Daddu(AT, AT, base); + base = AT; + offset &= (kMips64DoublewordSize - 1); + } + + switch (type) { + case kStoreByte: + Sb(reg, base, offset); + break; + case kStoreHalfword: + Sh(reg, base, offset); + break; + case kStoreWord: + CHECK_ALIGNED(offset, kMips64WordSize); + Sw(reg, base, offset); + break; + case kStoreDoubleword: + if (!IsAligned<kMips64DoublewordSize>(offset)) { + CHECK_ALIGNED(offset, kMips64WordSize); + Sw(reg, base, offset); + null_checker(); + Dsrl32(TMP2, reg, 0); + Sw(TMP2, base, offset + kMips64WordSize); + } else { + Sd(reg, base, offset); + null_checker(); + } + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } + if (type != kStoreDoubleword) { + null_checker(); + } + } + + template <typename ImplicitNullChecker = NoImplicitNullChecker> + void StoreFpuToOffset(StoreOperandType type, + FpuRegister reg, + GpuRegister base, + int32_t offset, + ImplicitNullChecker null_checker = NoImplicitNullChecker()) { + if (!IsInt<16>(offset) || + (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) && + !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) { + LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1)); + Daddu(AT, AT, base); + base = AT; + offset &= (kMips64DoublewordSize - 1); + } + + switch (type) { + case kStoreWord: + CHECK_ALIGNED(offset, kMips64WordSize); + Swc1(reg, base, offset); + null_checker(); + break; + case kStoreDoubleword: + if (!IsAligned<kMips64DoublewordSize>(offset)) { + CHECK_ALIGNED(offset, kMips64WordSize); + Mfhc1(TMP2, reg); + Swc1(reg, base, offset); + null_checker(); + Sw(TMP2, base, offset + kMips64WordSize); + } else { + Sdc1(reg, base, offset); + null_checker(); + } + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } + } + void LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base, int32_t offset); void LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base, int32_t offset); void StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base, int32_t offset); diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 2d084c1990..952909c1e2 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -41,7 +41,7 @@ static const char kDexFileLayoutInputDex[] = "AAAAdQEAAAAQAAABAAAAjAEAAA=="; static const char kDexFileLayoutInputProfile[] = - "cHJvADAwMwABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA=="; + "cHJvADAwNAABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA=="; static const char kDexFileLayoutExpectedOutputDex[] = "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH" diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index d395c170bf..5a758aedf4 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -16,11 +16,14 @@ #include <gtest/gtest.h> +#include "art_method-inl.h" #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" #include "exec_utils.h" -#include "profile_assistant.h" #include "jit/profile_compilation_info.h" +#include "mirror/class-inl.h" +#include "profile_assistant.h" +#include "scoped_thread_state_change-inl.h" #include "utils.h" namespace art { @@ -95,10 +98,12 @@ class ProfileAssistantTest : public CommonRuntimeTest { return ExecAndReturnCode(argv_str, &error); } - bool CreateProfile(std::string class_file_contents, const std::string& filename) { + bool CreateProfile(std::string profile_file_contents, + const std::string& filename, + const std::string& dex_location) { ScratchFile class_names_file; File* file = class_names_file.GetFile(); - EXPECT_TRUE(file->WriteFully(class_file_contents.c_str(), class_file_contents.length())); + EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length())); EXPECT_EQ(0, file->Flush()); EXPECT_TRUE(file->ResetOffset()); std::string profman_cmd = GetProfmanCmd(); @@ -106,8 +111,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { argv_str.push_back(profman_cmd); argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename()); argv_str.push_back("--reference-profile-file=" + filename); - argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); - argv_str.push_back("--dex-location=classes.dex"); + argv_str.push_back("--apk=" + dex_location); + argv_str.push_back("--dex-location=" + dex_location); std::string error; EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); return true; @@ -121,7 +126,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { argv_str.push_back("--dump-classes"); argv_str.push_back("--profile-file=" + filename); argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); - argv_str.push_back("--dex-location=classes.dex"); + argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]); argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(class_names_file))); std::string error; EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); @@ -137,11 +142,72 @@ class ProfileAssistantTest : public CommonRuntimeTest { bool CreateAndDump(const std::string& input_file_contents, std::string* output_file_contents) { ScratchFile profile_file; - EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename())); + EXPECT_TRUE(CreateProfile(input_file_contents, + profile_file.GetFilename(), + GetLibCoreDexFileNames()[0])); profile_file.GetFile()->ResetOffset(); EXPECT_TRUE(DumpClasses(profile_file.GetFilename(), output_file_contents)); return true; } + + mirror::Class* GetClass(jobject class_loader, const std::string& clazz) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_loader( + hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); + return class_linker->FindClass(self, clazz.c_str(), h_loader); + } + + ArtMethod* GetVirtualMethod(jobject class_loader, + const std::string& clazz, + const std::string& name) { + mirror::Class* klass = GetClass(class_loader, clazz); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + const auto pointer_size = class_linker->GetImagePointerSize(); + ArtMethod* method = nullptr; + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + if (name == m.GetName()) { + EXPECT_TRUE(method == nullptr); + method = &m; + } + } + return method; + } + + // Verify that given method has the expected inline caches and nothing else. + void AssertInlineCaches(ArtMethod* method, + const std::set<mirror::Class*>& expected_clases, + const ProfileCompilationInfo& info, + bool megamorphic) + REQUIRES_SHARED(Locks::mutator_lock_) { + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + ASSERT_TRUE(info.GetMethod(method->GetDexFile()->GetLocation(), + method->GetDexFile()->GetLocationChecksum(), + method->GetDexMethodIndex(), + &pmi)); + ASSERT_EQ(pmi.inline_caches.size(), 1u); + ProfileCompilationInfo::DexPcData dex_pc_data = pmi.inline_caches.begin()->second; + + ASSERT_EQ(dex_pc_data.is_megamorphic, megamorphic); + ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size()); + size_t found = 0; + for (mirror::Class* it : expected_clases) { + for (const auto& class_ref : dex_pc_data.classes) { + ProfileCompilationInfo::DexReference dex_ref = + pmi.dex_references[class_ref.dex_profile_index]; + if (dex_ref.MatchesDex(&(it->GetDexFile())) && + class_ref.type_index == it->GetDexTypeIndex()) { + found++; + } + } + } + + ASSERT_EQ(expected_clases.size(), found); + } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { @@ -358,25 +424,28 @@ TEST_F(ProfileAssistantTest, TestProfileGeneration) { TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) { // Class names put here need to be in sorted order. std::vector<std::string> class_names = { - "java.lang.Comparable", - "java.lang.Math", - "java.lang.Object" + "Ljava/lang/Comparable;", + "Ljava/lang/Math;", + "Ljava/lang/Object;" }; std::string input_file_contents; + std::string expected_contents; for (std::string& class_name : class_names) { input_file_contents += class_name + std::string("\n"); + expected_contents += DescriptorToDot(class_name.c_str()) + + std::string("\n"); } std::string output_file_contents; ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); - ASSERT_EQ(output_file_contents, input_file_contents); + ASSERT_EQ(output_file_contents, expected_contents); } TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) { // Class names put here need to be in sorted order. std::vector<std::string> class_names = { - "doesnt.match.this.one", - "java.lang.Comparable", - "java.lang.Object" + "Ldoesnt/match/this/one;", + "Ljava/lang/Comparable;", + "Ljava/lang/Object;" }; std::string input_file_contents; for (std::string& class_name : class_names) { @@ -385,16 +454,17 @@ TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) { std::string output_file_contents; ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); std::string expected_contents = - class_names[1] + std::string("\n") + class_names[2] + std::string("\n"); + DescriptorToDot(class_names[1].c_str()) + std::string("\n") + + DescriptorToDot(class_names[2].c_str()) + std::string("\n"); ASSERT_EQ(output_file_contents, expected_contents); } TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) { // Class names put here need to be in sorted order. std::vector<std::string> class_names = { - "doesnt.match.this.one", - "doesnt.match.this.one.either", - "nor.this.one" + "Ldoesnt/match/this/one;", + "Ldoesnt/match/this/one/either;", + "Lnor/this/one;" }; std::string input_file_contents; for (std::string& class_name : class_names) { @@ -406,4 +476,88 @@ TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) { ASSERT_EQ(output_file_contents, expected_contents); } +TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { + // Create the profile content. + std::vector<std::string> methods = { + "LTestInline;->inlineMonomorphic(LSuper;)I+LSubA;", + "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;", + "LTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;", + "LTestInline;->noInlineCache(LSuper;)I" + }; + std::string input_file_contents; + for (std::string& m : methods) { + input_file_contents += m + std::string("\n"); + } + + // Create the profile and save it to disk. + ScratchFile profile_file; + ASSERT_TRUE(CreateProfile(input_file_contents, + profile_file.GetFilename(), + GetTestDexFileName("ProfileTestMultiDex"))); + + // Load the profile from disk. + ProfileCompilationInfo info; + profile_file.GetFile()->ResetOffset(); + ASSERT_TRUE(info.Load(GetFd(profile_file))); + + // Load the dex files and verify that the profile contains the expected methods info. + ScopedObjectAccess soa(Thread::Current()); + jobject class_loader = LoadDex("ProfileTestMultiDex"); + ASSERT_NE(class_loader, nullptr); + + mirror::Class* sub_a = GetClass(class_loader, "LSubA;"); + mirror::Class* sub_b = GetClass(class_loader, "LSubB;"); + mirror::Class* sub_c = GetClass(class_loader, "LSubC;"); + + ASSERT_TRUE(sub_a != nullptr); + ASSERT_TRUE(sub_b != nullptr); + ASSERT_TRUE(sub_c != nullptr); + + { + // Verify that method inlineMonomorphic has the expected inline caches and nothing else. + ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader, + "LTestInline;", + "inlineMonomorphic"); + ASSERT_TRUE(inline_monomorphic != nullptr); + std::set<mirror::Class*> expected_monomorphic; + expected_monomorphic.insert(sub_a); + AssertInlineCaches(inline_monomorphic, expected_monomorphic, info, /*megamorphic*/ false); + } + + { + // Verify that method inlinePolymorphic has the expected inline caches and nothing else. + ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader, + "LTestInline;", + "inlinePolymorphic"); + ASSERT_TRUE(inline_polymorhic != nullptr); + std::set<mirror::Class*> expected_polymorphic; + expected_polymorphic.insert(sub_a); + expected_polymorphic.insert(sub_b); + expected_polymorphic.insert(sub_c); + AssertInlineCaches(inline_polymorhic, expected_polymorphic, info, /*megamorphic*/ false); + } + + { + // Verify that method inlineMegamorphic has the expected inline caches and nothing else. + ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader, + "LTestInline;", + "inlineMegamorphic"); + ASSERT_TRUE(inline_megamorphic != nullptr); + std::set<mirror::Class*> expected_megamorphic; + AssertInlineCaches(inline_megamorphic, expected_megamorphic, info, /*megamorphic*/ true); + } + + { + // Verify that method noInlineCache has no inline caches in the profile. + ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache"); + ASSERT_TRUE(no_inline_cache != nullptr); + ProfileCompilationInfo::OfflineProfileMethodInfo pmi_no_inline_cache; + ASSERT_TRUE(info.GetMethod(no_inline_cache->GetDexFile()->GetLocation(), + no_inline_cache->GetDexFile()->GetLocationChecksum(), + no_inline_cache->GetDexMethodIndex(), + &pmi_no_inline_cache)); + ASSERT_TRUE(pmi_no_inline_cache.inline_caches.empty()); + } +} + } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index a42e4f1db1..a99a0ea2da 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -36,6 +36,7 @@ #include "base/stringpiece.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" +#include "bytecode_utils.h" #include "dex_file.h" #include "jit/profile_compilation_info.h" #include "runtime.h" @@ -136,6 +137,14 @@ static constexpr uint16_t kDefaultTestProfileNumDex = 20; static constexpr uint16_t kDefaultTestProfileMethodRatio = 5; static constexpr uint16_t kDefaultTestProfileClassRatio = 5; +// Separators used when parsing human friendly representation of profiles. +static const std::string kMethodSep = "->"; +static constexpr char kProfileParsingInlineChacheSep = '+'; +static constexpr char kProfileParsingTypeSep = ','; +static constexpr char kProfileParsingFirstCharInSignature = '('; + +// TODO(calin): This class has grown too much from its initial design. Split the functionality +// into smaller, more contained pieces. class ProfMan FINAL { public: ProfMan() : @@ -522,6 +531,180 @@ class ProfMan FINAL { return output.release(); } + // Find class klass_descriptor in the given dex_files and store its reference + // in the out parameter class_ref. + // Return true if the definition of the class was found in any of the dex_files. + bool FindClass(const std::vector<std::unique_ptr<const DexFile>>& dex_files, + const std::string& klass_descriptor, + /*out*/ProfileMethodInfo::ProfileClassReference* class_ref) { + for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) { + const DexFile* dex_file = dex_file_ptr.get(); + const DexFile::TypeId* type_id = dex_file->FindTypeId(klass_descriptor.c_str()); + if (type_id == nullptr) { + continue; + } + dex::TypeIndex type_index = dex_file->GetIndexForTypeId(*type_id); + if (dex_file->FindClassDef(type_index) == nullptr) { + // Class is only referenced in the current dex file but not defined in it. + continue; + } + class_ref->dex_file = dex_file; + class_ref->type_index = type_index; + return true; + } + return false; + } + + // Find the method specified by method_spec in the class class_ref. The method + // must have a single INVOKE_VIRTUAL in its byte code. + // Upon success it returns true and stores the method index and the invoke dex pc + // in the output parameters. + // The format of the method spec is "inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;". + // + // TODO(calin): support INVOKE_INTERFACE and the range variants. + bool FindMethodWithSingleInvoke(const ProfileMethodInfo::ProfileClassReference& class_ref, + const std::string& method_spec, + /*out*/uint16_t* method_index, + /*out*/uint32_t* dex_pc) { + std::vector<std::string> name_and_signature; + Split(method_spec, kProfileParsingFirstCharInSignature, &name_and_signature); + if (name_and_signature.size() != 2) { + LOG(ERROR) << "Invalid method name and signature " << method_spec; + } + const std::string& name = name_and_signature[0]; + const std::string& signature = kProfileParsingFirstCharInSignature + name_and_signature[1]; + const DexFile* dex_file = class_ref.dex_file; + + const DexFile::StringId* name_id = dex_file->FindStringId(name.c_str()); + if (name_id == nullptr) { + LOG(ERROR) << "Could not find name: " << name; + return false; + } + dex::TypeIndex return_type_idx; + std::vector<dex::TypeIndex> param_type_idxs; + if (!dex_file->CreateTypeList(signature, &return_type_idx, ¶m_type_idxs)) { + LOG(ERROR) << "Could not create type list" << signature; + return false; + } + const DexFile::ProtoId* proto_id = dex_file->FindProtoId(return_type_idx, param_type_idxs); + if (proto_id == nullptr) { + LOG(ERROR) << "Could not find proto_id: " << name; + return false; + } + const DexFile::MethodId* method_id = dex_file->FindMethodId( + dex_file->GetTypeId(class_ref.type_index), *name_id, *proto_id); + if (method_id == nullptr) { + LOG(ERROR) << "Could not find method_id: " << name; + return false; + } + + *method_index = dex_file->GetIndexForMethodId(*method_id); + + uint32_t offset = dex_file->FindCodeItemOffset( + *dex_file->FindClassDef(class_ref.type_index), + *method_index); + const DexFile::CodeItem* code_item = dex_file->GetCodeItem(offset); + + bool found_invoke = false; + for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) { + if (it.CurrentInstruction().Opcode() == Instruction::INVOKE_VIRTUAL) { + if (found_invoke) { + LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: " << name; + return false; + } + found_invoke = true; + *dex_pc = it.CurrentDexPc(); + } + } + if (!found_invoke) { + LOG(ERROR) << "Could not find any INVOKE_VIRTUAL: " << name; + } + return found_invoke; + } + + // Process a line defining a class or a method and its inline caches. + // Upon success return true and add the class or the method info to profile. + // The format of the method line is: + // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;". + // The method and classes are searched only in the given dex files. + bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files, + const std::string& line, + /*out*/ProfileCompilationInfo* profile) { + std::string klass; + std::string method_str; + size_t method_sep_index = line.find(kMethodSep); + if (method_sep_index == std::string::npos) { + klass = line; + } else { + klass = line.substr(0, method_sep_index); + method_str = line.substr(method_sep_index + kMethodSep.size()); + } + + ProfileMethodInfo::ProfileClassReference class_ref; + if (!FindClass(dex_files, klass, &class_ref)) { + LOG(ERROR) << "Could not find class: " << klass; + return false; + } + + if (method_str.empty()) { + // No method to add. Just add the class. + std::set<DexCacheResolvedClasses> resolved_class_set; + const DexFile* dex_file = class_ref.dex_file; + const auto& dex_resolved_classes = resolved_class_set.emplace( + dex_file->GetLocation(), + dex_file->GetBaseLocation(), + dex_file->GetLocationChecksum()); + dex_resolved_classes.first->AddClass(class_ref.type_index); + profile->AddMethodsAndClasses(std::vector<ProfileMethodInfo>(), resolved_class_set); + return true; + } + + // Process the method. + std::string method_spec; + std::vector<std::string> inline_cache_elems; + + std::vector<std::string> method_elems; + Split(method_str, kProfileParsingInlineChacheSep, &method_elems); + if (method_elems.size() == 2) { + method_spec = method_elems[0]; + Split(method_elems[1], kProfileParsingTypeSep, &inline_cache_elems); + } else if (method_elems.size() == 1) { + method_spec = method_elems[0]; + } else { + LOG(ERROR) << "Invalid method line: " << line; + return false; + } + + uint16_t method_index; + uint32_t dex_pc; + if (!FindMethodWithSingleInvoke(class_ref, method_spec, &method_index, &dex_pc)) { + return false; + } + std::vector<ProfileMethodInfo::ProfileClassReference> classes(inline_cache_elems.size()); + size_t class_it = 0; + for (const std::string ic_class : inline_cache_elems) { + if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) { + LOG(ERROR) << "Could not find class: " << ic_class; + return false; + } + } + std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; + inline_caches.emplace_back(dex_pc, classes); + std::vector<ProfileMethodInfo> pmi; + pmi.emplace_back(class_ref.dex_file, method_index, inline_caches); + + profile->AddMethodsAndClasses(pmi, std::set<DexCacheResolvedClasses>()); + return true; + } + + // Creates a profile from a human friendly textual representation. + // The expected input format is: + // # Classes + // Ljava/lang/Comparable; + // Ljava/lang/Math; + // # Methods with inline caches + // LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC; + // LTestInline;->noInlineCache(LSuper;)I int CreateProfile() { // Validate parameters for this command. if (apk_files_.empty() && apks_fd_.empty()) { @@ -550,51 +733,22 @@ class ProfMan FINAL { return -1; } } - // Read the user-specified list of classes (dot notation rather than descriptors). + // Read the user-specified list of classes and methods. std::unique_ptr<std::unordered_set<std::string>> - user_class_list(ReadCommentedInputFromFile<std::unordered_set<std::string>>( + user_lines(ReadCommentedInputFromFile<std::unordered_set<std::string>>( create_profile_from_file_.c_str(), nullptr)); // No post-processing. - std::unordered_set<std::string> matched_user_classes; - // Open the dex files to look up class names. + + // Open the dex files to look up classes and methods. std::vector<std::unique_ptr<const DexFile>> dex_files; OpenApkFilesFromLocations(&dex_files); - // Iterate over the dex files looking for class names in the input stream. - std::set<DexCacheResolvedClasses> resolved_class_set; - for (auto& dex_file : dex_files) { - // Compute the set of classes to be added for this dex file first. This - // avoids creating an entry in the profile information for dex files that - // contribute no classes. - std::unordered_set<dex::TypeIndex> classes_to_be_added; - for (const auto& klass : *user_class_list) { - std::string descriptor = DotToDescriptor(klass.c_str()); - const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor.c_str()); - if (type_id == nullptr) { - continue; - } - classes_to_be_added.insert(dex_file->GetIndexForTypeId(*type_id)); - matched_user_classes.insert(klass); - } - if (classes_to_be_added.empty()) { - continue; - } - // Insert the DexCacheResolved Classes into the set expected for - // AddMethodsAndClasses. - std::set<DexCacheResolvedClasses>::iterator dex_resolved_classes = - resolved_class_set.emplace(dex_file->GetLocation(), - dex_file->GetBaseLocation(), - dex_file->GetLocationChecksum()).first; - dex_resolved_classes->AddClasses(classes_to_be_added.begin(), classes_to_be_added.end()); - } - // Warn the user if we didn't find matches for every class. - for (const auto& klass : *user_class_list) { - if (matched_user_classes.find(klass) == matched_user_classes.end()) { - LOG(WARNING) << "requested class '" << klass << "' was not matched in any dex file"; - } - } - // Generate the profile data structure. + + // Process the lines one by one and add the successful ones to the profile. ProfileCompilationInfo info; - std::vector<ProfileMethodInfo> methods; // No methods for now. - info.AddMethodsAndClasses(methods, resolved_class_set); + + for (const auto& line : *user_lines) { + ProcessLine(dex_files, line, &info); + } + // Write the profile file. CHECK(info.Save(fd)); if (close(fd) < 0) { diff --git a/compiler/optimizing/bytecode_utils.h b/runtime/bytecode_utils.h index 133afa47fe..fa87b1d6da 100644 --- a/compiler/optimizing/bytecode_utils.h +++ b/runtime/bytecode_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_ -#define ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_ +#ifndef ART_RUNTIME_BYTECODE_UTILS_H_ +#define ART_RUNTIME_BYTECODE_UTILS_H_ #include "base/arena_object.h" #include "dex_file.h" @@ -177,4 +177,4 @@ inline bool IsThrowingDexInstruction(const Instruction& instruction) { } // namespace art -#endif // ART_COMPILER_OPTIMIZING_BYTECODE_UTILS_H_ +#endif // ART_RUNTIME_BYTECODE_UTILS_H_ diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h index eef4fba20d..f0e1029f85 100644 --- a/runtime/gc/collector_type.h +++ b/runtime/gc/collector_type.h @@ -59,6 +59,8 @@ enum CollectorType { kCollectorTypeHprof, // Fake collector for installing/removing a system-weak holder. kCollectorTypeAddRemoveSystemWeakHolder, + // Fake collector type for GetObjectsAllocated + kCollectorTypeGetObjectsAllocated, }; std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type); diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 9e34346686..c1c1cad861 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -40,6 +40,7 @@ const char* PrettyCause(GcCause cause) { case kGcCauseJitCodeCache: return "JitCodeCache"; case kGcCauseAddRemoveSystemWeakHolder: return "SystemWeakHolder"; case kGcCauseHprof: return "Hprof"; + case kGcCauseGetObjectsAllocated: return "ObjectsAllocated"; } LOG(FATAL) << "Unreachable"; UNREACHABLE(); diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h index 9b285b12a4..eb27547768 100644 --- a/runtime/gc/gc_cause.h +++ b/runtime/gc/gc_cause.h @@ -53,8 +53,10 @@ enum GcCause { kGcCauseJitCodeCache, // Not a real GC cause, used to add or remove system-weak holders. kGcCauseAddRemoveSystemWeakHolder, - // Not a real GC cause, used to hprof running in the middle of GC. + // Not a real GC cause, used to prevent hprof running in the middle of GC. kGcCauseHprof, + // Not a real GC cause, used to prevent GetObjectsAllocated running in the middle of GC. + kGcCauseGetObjectsAllocated, }; const char* PrettyCause(GcCause cause); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index b857ea3eef..a7697484e2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1835,6 +1835,11 @@ void Heap::SetTargetHeapUtilization(float target) { size_t Heap::GetObjectsAllocated() const { Thread* const self = Thread::Current(); ScopedThreadStateChange tsc(self, kWaitingForGetObjectsAllocated); + // Prevent GC running during GetObjectsALlocated since we may get a checkpoint request that tells + // us to suspend while we are doing SuspendAll. b/35232978 + gc::ScopedGCCriticalSection gcs(Thread::Current(), + gc::kGcCauseGetObjectsAllocated, + gc::kCollectorTypeGetObjectsAllocated); // Need SuspendAll here to prevent lock violation if RosAlloc does it during InspectAll. ScopedSuspendAll ssa(__FUNCTION__); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 3467b60343..8c5bb0d775 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -37,7 +37,8 @@ namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; -const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '3', '\0' }; // inline caches +// Last profile version: fix the order of dex files in the profile. +const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '4', '\0' }; static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -222,15 +223,23 @@ bool ProfileCompilationInfo::Save(int fd) { DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max()); AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size())); + // Make sure we write the dex files in order of their profile index. This + // avoids writing the index in the output file and simplifies the parsing logic. + std::vector<const std::string*> ordered_info_location(info_.size()); + std::vector<const DexFileData*> ordered_info_data(info_.size()); for (const auto& it : info_) { + ordered_info_location[it.second.profile_index] = &(it.first); + ordered_info_data[it.second.profile_index] = &(it.second); + } + for (size_t i = 0; i < info_.size(); i++) { if (buffer.size() > kMaxSizeToKeepBeforeWriting) { if (!WriteBuffer(fd, buffer.data(), buffer.size())) { return false; } buffer.clear(); } - const std::string& dex_location = it.first; - const DexFileData& dex_data = it.second; + const std::string& dex_location = *ordered_info_location[i]; + const DexFileData& dex_data = *ordered_info_data[i]; // Note that we allow dex files without any methods or classes, so that // inline caches can refer valid dex files. @@ -797,10 +806,13 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { SafeMap<uint8_t, uint8_t> dex_profile_index_remap; for (const auto& other_it : other.info_) { const std::string& other_dex_location = other_it.first; + uint32_t other_checksum = other_it.second.checksum; const DexFileData& other_dex_data = other_it.second; - auto info_it = info_.FindOrAdd(other_dex_location, DexFileData(other_dex_data.checksum, 0)); - const DexFileData& dex_data = info_it->second; - dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data.profile_index); + const DexFileData* dex_data = GetOrAddDexFileData(other_dex_location, other_checksum); + if (dex_data == nullptr) { + return false; // Could happen if we exceed the number of allowed dex files. + } + dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data->profile_index); } // Merge the actual profile data. @@ -949,10 +961,17 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* os << "ProfileInfo:"; const std::string kFirstDexFileKeySubstitute = ":classes.dex"; + // Write the entries in profile index order. + std::vector<const std::string*> ordered_info_location(info_.size()); + std::vector<const DexFileData*> ordered_info_data(info_.size()); for (const auto& it : info_) { + ordered_info_location[it.second.profile_index] = &(it.first); + ordered_info_data[it.second.profile_index] = &(it.second); + } + for (size_t profile_index = 0; profile_index < info_.size(); profile_index++) { os << "\n"; - const std::string& location = it.first; - const DexFileData& dex_data = it.second; + const std::string& location = *ordered_info_location[profile_index]; + const DexFileData& dex_data = *ordered_info_data[profile_index]; if (print_full_dex_location) { os << location; } else { @@ -960,6 +979,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* std::string multidex_suffix = DexFile::GetMultiDexSuffix(location); os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix); } + os << " [index=" << static_cast<uint32_t>(dex_data.profile_index) << "]"; const DexFile* dex_file = nullptr; if (dex_files != nullptr) { for (size_t i = 0; i < dex_files->size(); i++) { @@ -1022,7 +1042,8 @@ void ProfileCompilationInfo::GetClassNames(const std::vector<const DexFile*>* de const DexFile* dex_file = nullptr; if (dex_files != nullptr) { for (size_t i = 0; i < dex_files->size(); i++) { - if (location == (*dex_files)[i]->GetLocation()) { + if (location == GetProfileDexFileKey((*dex_files)[i]->GetLocation()) && + dex_data.checksum == (*dex_files)[i]->GetLocationChecksum()) { dex_file = (*dex_files)[i]; } } diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index cf556bf3e1..f089dff898 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -36,11 +36,12 @@ namespace art { */ struct ProfileMethodInfo { struct ProfileClassReference { + ProfileClassReference() : dex_file(nullptr) {} ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index) : dex_file(dex), type_index(index) {} const DexFile* dex_file; - const dex::TypeIndex type_index; + dex::TypeIndex type_index; }; struct ProfileInlineCache { @@ -91,6 +92,11 @@ class ProfileCompilationInfo { return dex_checksum == other.dex_checksum && dex_location == other.dex_location; } + bool MatchesDex(const DexFile* dex_file) const { + return dex_checksum == dex_file->GetLocationChecksum() && + dex_location == GetProfileDexFileKey(dex_file->GetLocation()); + } + std::string dex_location; uint32_t dex_checksum; }; diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index bfb04a4fba..e86340e91f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -222,6 +222,7 @@ define name-to-var $(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_') endef # name-to-var +# Disable 115-native-bridge, it fails when run through make b/35984597. # Disable 153-reference-stress temporarily until a fix arrives. b/33389022. # Disable 080-oom-fragmentation due to flakes. b/33795328 # Disable 497-inlining-and-class-loader and 542-unresolved-access-check until @@ -229,6 +230,7 @@ endef # name-to-var # register a dex file that's already registered with a different loader. # b/34193123 ART_TEST_RUN_TEST_SKIP += \ + 115-native-bridge \ 153-reference-stress \ 080-oom-fragmentation \ 497-inlining-and-class-loader \ diff --git a/test/ProfileTestMultiDex/Main.java b/test/ProfileTestMultiDex/Main.java index 41532ea8f7..73fbb00d25 100644 --- a/test/ProfileTestMultiDex/Main.java +++ b/test/ProfileTestMultiDex/Main.java @@ -25,3 +25,41 @@ class Main { return "C"; } } + +class TestInline { + public int inlineMonomorphic(Super s) { + return s.getValue(); + } + + public int inlinePolymorphic(Super s) { + return s.getValue(); + } + + public int inlineMegamorphic(Super s) { + return s.getValue(); + } + + public int noInlineCache(Super s) { + return s.getValue(); + } +} + +abstract class Super { + abstract int getValue(); +} + +class SubA extends Super { + int getValue() { return 42; } +} + +class SubB extends Super { + int getValue() { return 38; }; +} + +class SubD extends Super { + int getValue() { return 20; }; +} + +class SubE extends Super { + int getValue() { return 16; }; +} diff --git a/test/ProfileTestMultiDex/Second.java b/test/ProfileTestMultiDex/Second.java index 4ac5abc300..4b3c7a479b 100644 --- a/test/ProfileTestMultiDex/Second.java +++ b/test/ProfileTestMultiDex/Second.java @@ -25,3 +25,8 @@ class Second { return "Z"; } } + +class SubC extends Super { + int getValue() { return 24; } +} + diff --git a/test/ProfileTestMultiDex/main.jpp b/test/ProfileTestMultiDex/main.jpp index f2e3b4e14c..5e55e96874 100644 --- a/test/ProfileTestMultiDex/main.jpp +++ b/test/ProfileTestMultiDex/main.jpp @@ -1,3 +1,21 @@ -main: +Main: @@com.android.jack.annotations.ForceInMainDex - class Second + class Main +TestInqline: + @@com.android.jack.annotations.ForceInMainDex + class TestInline +Super: + @@com.android.jack.annotations.ForceInMainDex + class Super +SubA: + @@com.android.jack.annotations.ForceInMainDex + class SubA +SubB: + @@com.android.jack.annotations.ForceInMainDex + class SubB +SubD: + @@com.android.jack.annotations.ForceInMainDex + class SubD +SubE: + @@com.android.jack.annotations.ForceInMainDex + class SubE diff --git a/test/ProfileTestMultiDex/main.list b/test/ProfileTestMultiDex/main.list index 44ba78ead5..ec131f0f71 100644 --- a/test/ProfileTestMultiDex/main.list +++ b/test/ProfileTestMultiDex/main.list @@ -1 +1,7 @@ Main.class +TestInline.class +Super.class +SubA.class +SubB.class +SubD.class +SubE.class |