diff options
105 files changed, 2124 insertions, 1110 deletions
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 239bc590e9..6075cd6fbe 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -187,7 +187,9 @@ void CommonCompilerTest::SetUp() { } } -void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) { +void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, + InstructionSet isa, + size_t number_of_threads) { compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), @@ -198,7 +200,7 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe GetImageClasses(), GetCompiledClasses(), GetCompiledMethods(), - /* thread_count */ 2, + number_of_threads, /* dump_stats */ true, /* dump_passes */ true, timer_.get(), diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 7e0fbabff8..9552143080 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -93,7 +93,7 @@ class CommonCompilerTest : public CommonRuntimeTest { const char* method_name, const char* signature) SHARED_REQUIRES(Locks::mutator_lock_); - void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa); + void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U); void ReserveImageSpace(); diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h index 58502a3f9c..b4a4d63f01 100644 --- a/compiler/debug/dwarf/debug_line_opcode_writer.h +++ b/compiler/debug/dwarf/debug_line_opcode_writer.h @@ -36,7 +36,7 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> { public: static constexpr int kOpcodeBase = 13; - static constexpr bool kDefaultIsStmt = true; + static constexpr bool kDefaultIsStmt = false; static constexpr int kLineBase = -5; static constexpr int kLineRange = 14; @@ -81,8 +81,11 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> { this->PushUleb128(column); } - void NegateStmt() { - this->PushUint8(DW_LNS_negate_stmt); + void SetIsStmt(bool is_stmt) { + if (is_stmt_ != is_stmt) { + this->PushUint8(DW_LNS_negate_stmt); + is_stmt_ = is_stmt; + } } void SetBasicBlock() { @@ -112,6 +115,7 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> { current_address_ = 0; current_file_ = 1; current_line_ = 1; + is_stmt_ = kDefaultIsStmt; } // Uncoditionally set address using the long encoding. @@ -227,7 +231,8 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> { code_factor_bits_(codeFactorBits), current_address_(0), current_file_(1), - current_line_(1) { + current_line_(1), + is_stmt_(kDefaultIsStmt) { } private: @@ -244,6 +249,7 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> { uint64_t current_address_; int current_file_; int current_line_; + bool is_stmt_; std::vector<uintptr_t> patch_locations_; DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter); diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index e455d0d617..2ba3af5e10 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -217,7 +217,9 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("Advance Line by 2 to 3"); opcodes.SetColumn(4); DW_CHECK_NEXT("Set column to 4"); - opcodes.NegateStmt(); + opcodes.SetIsStmt(true); + DW_CHECK_NEXT("Set is_stmt to 1"); + opcodes.SetIsStmt(false); DW_CHECK_NEXT("Set is_stmt to 0"); opcodes.SetBasicBlock(); DW_CHECK_NEXT("Set basic block"); diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index 41bfe79c21..e2f0a65ab7 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -62,7 +62,7 @@ class DwarfTest : public CommonRuntimeTest { InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86; ScratchFile file; FileOutputStream output_stream(file.GetFile()); - ElfBuilder<ElfTypes> builder(isa, &output_stream); + ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream); builder.Start(); if (!debug_info_data_.empty()) { builder.WriteSection(".debug_info", &debug_info_data_); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index d3859ca752..11be4e9844 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -184,6 +184,10 @@ class ElfDebugLineWriter { // Generate mapping opcodes from PC to Java lines. if (file_index != 0) { + // If the method was not compiled as native-debuggable, we still generate all available + // lines, but we try to prevent the debugger from stepping and setting breakpoints since + // the information is too inaccurate for that (breakpoints would be set after the calls). + const bool default_is_stmt = mi->is_native_debuggable; bool first = true; for (SrcMapElem pc2dex : pc2dex_map) { uint32_t pc = pc2dex.from_; @@ -205,13 +209,14 @@ class ElfDebugLineWriter { // Assume that any preceding code is prologue. int first_line = dex2line_map.front().line_; // Prologue is not a sensible place for a breakpoint. - opcodes.NegateStmt(); + opcodes.SetIsStmt(false); opcodes.AddRow(method_address, first_line); - opcodes.NegateStmt(); opcodes.SetPrologueEnd(); } + opcodes.SetIsStmt(default_is_stmt); opcodes.AddRow(method_address + pc, line); } else if (line != opcodes.CurrentLine()) { + opcodes.SetIsStmt(default_is_stmt); opcodes.AddRow(method_address + pc, line); } } diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 01bd6797c9..79069d1aa2 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -91,24 +91,34 @@ static void WriteDebugSections(ElfBuilder<ElfTypes>* builder, std::vector<uint8_t> MakeMiniDebugInfo( InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_size, size_t text_size, const ArrayRef<const MethodDebugInfo>& method_infos) { if (Is64BitInstructionSet(isa)) { - return MakeMiniDebugInfoInternal<ElfTypes64>(isa, rodata_size, text_size, method_infos); + return MakeMiniDebugInfoInternal<ElfTypes64>(isa, + features, + rodata_size, + text_size, + method_infos); } else { - return MakeMiniDebugInfoInternal<ElfTypes32>(isa, rodata_size, text_size, method_infos); + return MakeMiniDebugInfoInternal<ElfTypes32>(isa, + features, + rodata_size, + text_size, + method_infos); } } template <typename ElfTypes> static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal( + const InstructionSet isa, + const InstructionSetFeatures* features, const MethodDebugInfo& method_info) { - const InstructionSet isa = method_info.compiled_method->GetInstructionSet(); std::vector<uint8_t> buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); + std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); WriteDebugInfo(builder.get(), @@ -124,23 +134,26 @@ static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal( return ArrayRef<const uint8_t>(result, buffer.size()); } -ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info) { - const InstructionSet isa = method_info.compiled_method->GetInstructionSet(); +ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const InstructionSet isa, + const InstructionSetFeatures* features, + const MethodDebugInfo& method_info) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodInternal<ElfTypes64>(method_info); + return WriteDebugElfFileForMethodInternal<ElfTypes64>(isa, features, method_info); } else { - return WriteDebugElfFileForMethodInternal<ElfTypes32>(method_info); + return WriteDebugElfFileForMethodInternal<ElfTypes32>(isa, features, method_info); } } template <typename ElfTypes> static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal( - const InstructionSet isa, const ArrayRef<mirror::Class*>& types) + const InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_) { std::vector<uint8_t> buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); + std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); ElfDebugInfoWriter<ElfTypes> info_writer(builder.get()); @@ -159,11 +172,12 @@ static ArrayRef<const uint8_t> WriteDebugElfFileForClassesInternal( } ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa, + const InstructionSetFeatures* features, const ArrayRef<mirror::Class*>& types) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, types); + return WriteDebugElfFileForClassesInternal<ElfTypes64>(isa, features, types); } else { - return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, types); + return WriteDebugElfFileForClassesInternal<ElfTypes32>(isa, features, types); } } diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index 103b501489..d364521f1b 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -37,13 +37,17 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, bool write_oat_patches); std::vector<uint8_t> MakeMiniDebugInfo(InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef<const MethodDebugInfo>& method_infos); -ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const MethodDebugInfo& method_info); +ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const InstructionSet isa, + const InstructionSetFeatures* features, + const MethodDebugInfo& method_info); ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa, + const InstructionSetFeatures* features, const ArrayRef<mirror::Class*>& types) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 5c7d1c72a4..fb63d62572 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -79,13 +79,14 @@ static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* ds template <typename ElfTypes> static std::vector<uint8_t> MakeMiniDebugInfoInternal( InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef<const MethodDebugInfo>& method_infos) { std::vector<uint8_t> buffer; buffer.reserve(KB); VectorOutputStream out("Mini-debug-info ELF file", &buffer); - std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); + std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out)); builder->Start(); // Mirror .rodata and .text as NOBITS sections. // It is needed to detected relocations after compression. diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index 6b3dd8c528..bb09f7e814 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -30,6 +30,7 @@ struct MethodDebugInfo { uint32_t access_flags; const DexFile::CodeItem* code_item; bool deduped; + bool is_native_debuggable; uintptr_t low_pc; uintptr_t high_pc; CompiledMethod* compiled_method; diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index ad4ddadd2f..8f5d3ae4bd 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -101,6 +101,14 @@ static constexpr bool kIntrinsicIsStatic[] = { false, // kIntrinsicCas false, // kIntrinsicUnsafeGet false, // kIntrinsicUnsafePut + false, // kIntrinsicUnsafeGetAndAddInt, + false, // kIntrinsicUnsafeGetAndAddLong, + false, // kIntrinsicUnsafeGetAndSetInt, + false, // kIntrinsicUnsafeGetAndSetLong, + false, // kIntrinsicUnsafeGetAndSetObject, + false, // kIntrinsicUnsafeLoadFence, + false, // kIntrinsicUnsafeStoreFence, + false, // kIntrinsicUnsafeFullFence, true, // kIntrinsicSystemArrayCopyCharArray true, // kIntrinsicSystemArrayCopy }; @@ -177,6 +185,14 @@ static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddInt], "UnsafeGetAndAddInt must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddLong], "UnsafeGetAndAddLong must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetInt], "UnsafeGetAndSetInt must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetLong], "UnsafeGetAndSetLong must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetObject], "UnsafeGetAndSetObject must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeLoadFence], "UnsafeLoadFence must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeStoreFence], "UnsafeStoreFence must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeFullFence], "UnsafeFullFence must not be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray], "SystemArrayCopyCharArray must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy], @@ -318,6 +334,14 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "putObject", // kNameCachePutObject "putObjectVolatile", // kNameCachePutObjectVolatile "putOrderedObject", // kNameCachePutOrderedObject + "getAndAddInt", // kNameCacheGetAndAddInt, + "getAndAddLong", // kNameCacheGetAndAddLong, + "getAndSetInt", // kNameCacheGetAndSetInt, + "getAndSetLong", // kNameCacheGetAndSetLong, + "getAndSetObject", // kNameCacheGetAndSetObject, + "loadFence", // kNameCacheLoadFence, + "storeFence", // kNameCacheStoreFence, + "fullFence", // kNameCacheFullFence, "arraycopy", // kNameCacheArrayCopy "bitCount", // kNameCacheBitCount "compare", // kNameCacheCompare @@ -404,10 +428,14 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, // kProtoCacheObjectJ_I { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJI_I + { kClassCacheInt, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, // kProtoCacheObjectJI_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, // kProtoCacheObjectJ_J { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJJ_J + { kClassCacheLong, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJJ_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJ_Object @@ -415,6 +443,9 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { // kProtoCacheObjectJObject_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheJavaLangObject } }, + // kProtoCacheObjectJObject_Object + { kClassCacheJavaLangObject, 3, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheJavaLangObject } }, // kProtoCacheCharArrayICharArrayII_V { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} }, @@ -609,6 +640,16 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), #undef UNSAFE_GET_PUT + // 1.8 + INTRINSIC(SunMiscUnsafe, GetAndAddInt, ObjectJI_I, kIntrinsicUnsafeGetAndAddInt, 0), + INTRINSIC(SunMiscUnsafe, GetAndAddLong, ObjectJJ_J, kIntrinsicUnsafeGetAndAddLong, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetInt, ObjectJI_I, kIntrinsicUnsafeGetAndSetInt, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetLong, ObjectJJ_J, kIntrinsicUnsafeGetAndSetLong, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetObject, ObjectJObject_Object, kIntrinsicUnsafeGetAndSetObject, 0), + INTRINSIC(SunMiscUnsafe, LoadFence, _V, kIntrinsicUnsafeLoadFence, 0), + INTRINSIC(SunMiscUnsafe, StoreFence, _V, kIntrinsicUnsafeStoreFence, 0), + INTRINSIC(SunMiscUnsafe, FullFence, _V, kIntrinsicUnsafeFullFence, 0), + INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray, 0), INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy, @@ -815,6 +856,14 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { case kIntrinsicRotateRight: case kIntrinsicRotateLeft: case kIntrinsicSignum: + case kIntrinsicUnsafeGetAndAddInt: + case kIntrinsicUnsafeGetAndAddLong: + case kIntrinsicUnsafeGetAndSetInt: + case kIntrinsicUnsafeGetAndSetLong: + case kIntrinsicUnsafeGetAndSetObject: + case kIntrinsicUnsafeLoadFence: + case kIntrinsicUnsafeStoreFence: + case kIntrinsicUnsafeFullFence: case kIntrinsicSystemArrayCopy: return false; // not implemented in quick. default: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index b465db2c54..34b56cd494 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -227,6 +227,14 @@ class DexFileMethodInliner { kNameCachePutObject, kNameCachePutObjectVolatile, kNameCachePutOrderedObject, + kNameCacheGetAndAddInt, + kNameCacheGetAndAddLong, + kNameCacheGetAndSetInt, + kNameCacheGetAndSetLong, + kNameCacheGetAndSetObject, + kNameCacheLoadFence, + kNameCacheStoreFence, + kNameCacheFullFence, kNameCacheArrayCopy, kNameCacheBitCount, kNameCacheCompare, @@ -282,11 +290,14 @@ class DexFileMethodInliner { kProtoCacheObjectJJJ_Z, kProtoCacheObjectJObjectObject_Z, kProtoCacheObjectJ_I, + kProtoCacheObjectJI_I, kProtoCacheObjectJI_V, kProtoCacheObjectJ_J, + kProtoCacheObjectJJ_J, kProtoCacheObjectJJ_V, kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, + kProtoCacheObjectJObject_Object, kProtoCacheCharArrayICharArrayII_V, kProtoCacheObjectIObjectII_V, kProtoCacheIICharArrayI_V, diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index a220959288..4db82a638d 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -159,10 +159,16 @@ class CompilerOptions FINAL { size_t GetInlineDepthLimit() const { return inline_depth_limit_; } + void SetInlineDepthLimit(size_t limit) { + inline_depth_limit_ = limit; + } size_t GetInlineMaxCodeUnits() const { return inline_max_code_units_; } + void SetInlineMaxCodeUnits(size_t units) { + inline_max_code_units_ = units; + } double GetTopKProfileThreshold() const { return top_k_profile_threshold_; diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index f7da609e5d..943e2a897d 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -20,6 +20,7 @@ #include <vector> #include "arch/instruction_set.h" +#include "arch/mips/instruction_set_features_mips.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/unix_file/fd_file.h" @@ -392,8 +393,9 @@ class ElfBuilder FINAL { } }; - ElfBuilder(InstructionSet isa, OutputStream* output) + ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output) : isa_(isa), + features_(features), stream_(output), rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), @@ -818,6 +820,7 @@ class ElfBuilder FINAL { } InstructionSet isa_; + const InstructionSetFeatures* features_; ErrorDelayingOutputStream stream_; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 19346ecc2b..e35662dfca 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -51,10 +51,12 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class DebugInfoTask : public Task { public: DebugInfoTask(InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef<const debug::MethodDebugInfo>& method_infos) : isa_(isa), + instruction_set_features_(features), rodata_section_size_(rodata_section_size), text_section_size_(text_section_size), method_infos_(method_infos) { @@ -62,6 +64,7 @@ class DebugInfoTask : public Task { void Run(Thread*) { result_ = debug::MakeMiniDebugInfo(isa_, + instruction_set_features_, rodata_section_size_, text_section_size_, method_infos_); @@ -73,6 +76,7 @@ class DebugInfoTask : public Task { private: InstructionSet isa_; + const InstructionSetFeatures* instruction_set_features_; size_t rodata_section_size_; size_t text_section_size_; const ArrayRef<const debug::MethodDebugInfo>& method_infos_; @@ -83,6 +87,7 @@ template <typename ElfTypes> class ElfWriterQuick FINAL : public ElfWriter { public: ElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file); ~ElfWriterQuick(); @@ -107,6 +112,7 @@ class ElfWriterQuick FINAL : public ElfWriter { std::vector<uint8_t>* buffer); private: + const InstructionSetFeatures* instruction_set_features_; const CompilerOptions* const compiler_options_; File* const elf_file_; size_t rodata_size_; @@ -121,27 +127,36 @@ class ElfWriterQuick FINAL : public ElfWriter { }; std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file) { if (Is64BitInstructionSet(instruction_set)) { - return MakeUnique<ElfWriterQuick<ElfTypes64>>(instruction_set, compiler_options, elf_file); + return MakeUnique<ElfWriterQuick<ElfTypes64>>(instruction_set, + features, + compiler_options, + elf_file); } else { - return MakeUnique<ElfWriterQuick<ElfTypes32>>(instruction_set, compiler_options, elf_file); + return MakeUnique<ElfWriterQuick<ElfTypes32>>(instruction_set, + features, + compiler_options, + elf_file); } } template <typename ElfTypes> ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file) : ElfWriter(), + instruction_set_features_(features), compiler_options_(compiler_options), elf_file_(elf_file), rodata_size_(0u), text_size_(0u), bss_size_(0u), output_stream_(MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file))), - builder_(new ElfBuilder<ElfTypes>(instruction_set, output_stream_.get())) {} + builder_(new ElfBuilder<ElfTypes>(instruction_set, features, output_stream_.get())) {} template <typename ElfTypes> ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {} @@ -205,7 +220,11 @@ void ElfWriterQuick<ElfTypes>::PrepareDebugInfo( // Prepare the mini-debug-info in background while we do other I/O. Thread* self = Thread::Current(); debug_info_task_ = std::unique_ptr<DebugInfoTask>( - new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos)); + new DebugInfoTask(builder_->GetIsa(), + instruction_set_features_, + rodata_size_, + text_size_, + method_infos)); debug_info_thread_pool_ = std::unique_ptr<ThreadPool>( new ThreadPool("Mini-debug-info writer", 1)); debug_info_thread_pool_->AddTask(self, debug_info_task_.get()); diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h index 347d372fe2..3d5dd39a66 100644 --- a/compiler/elf_writer_quick.h +++ b/compiler/elf_writer_quick.h @@ -26,8 +26,10 @@ namespace art { class CompilerOptions; +class InstructionSetFeatures; std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 5763cec43f..91579e9daf 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -24,6 +24,7 @@ #include "class_linker-inl.h" #include "common_compiler_test.h" #include "debug/method_debug_info.h" +#include "driver/compiler_options.h" #include "elf_writer.h" #include "elf_writer_quick.h" #include "gc/space/image_space.h" @@ -48,8 +49,12 @@ class ImageTest : public CommonCompilerTest { }; void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { - // TODO: Test does not currently work with optimizing. - CreateCompilerDriver(Compiler::kQuick, kRuntimeISA); + CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); + + // Set inline filter values. + compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit); + compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Enable write for dex2dex. for (const DexFile* dex_file : class_linker->GetBootClassPath()) { @@ -99,6 +104,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { const std::vector<const DexFile*>& dex_files = class_linker->GetBootClassPath(); std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), oat_file.GetFile()); elf_writer->Start(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 871435b85f..b1b971f6ba 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -266,17 +266,9 @@ bool ImageWriter::Write(int image_fd, << PrettyDuration(NanoTime() - compress_start_time); } - // Write header first, as uncompressed. - image_header->data_size_ = data_size; - if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) { - PLOG(ERROR) << "Failed to write image file header " << image_filename; - image_file->Erase(); - return false; - } - // Write out the image + fields + methods. const bool is_compressed = compressed_data != nullptr; - if (!image_file->WriteFully(image_data_to_write, data_size)) { + if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) { PLOG(ERROR) << "Failed to write image file data " << image_filename; image_file->Erase(); return false; @@ -291,13 +283,33 @@ bool ImageWriter::Write(int image_fd, if (!is_compressed) { CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset()); } - if (!image_file->Write(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()), - bitmap_section.Size(), - bitmap_position_in_file)) { + if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()), + bitmap_section.Size(), + bitmap_position_in_file)) { PLOG(ERROR) << "Failed to write image file " << image_filename; image_file->Erase(); return false; } + + int err = image_file->Flush(); + if (err < 0) { + PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err; + image_file->Erase(); + return false; + } + + // Write header last in case the compiler gets killed in the middle of image writing. + // We do not want to have a corrupted image with a valid header. + // The header is uncompressed since it contains whether the image is compressed or not. + image_header->data_size_ = data_size; + if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()), + sizeof(ImageHeader), + 0)) { + PLOG(ERROR) << "Failed to write image file header " << image_filename; + image_file->Erase(); + return false; + } + CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), static_cast<size_t>(image_file->GetLength())); if (image_file->FlushCloseOrErase() != 0) { diff --git a/compiler/image_writer.h b/compiler/image_writer.h index dba9dd71fc..f204b28380 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -443,7 +443,7 @@ class ImageWriter FINAL { static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type); - uintptr_t NativeOffsetInImage(void* obj); + uintptr_t NativeOffsetInImage(void* obj) SHARED_REQUIRES(Locks::mutator_lock_); // Location of where the object will be when the image is loaded at runtime. template <typename T> diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index eeb25763a7..cda2e274ce 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -69,7 +69,8 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou DCHECK(jit_compiler != nullptr); if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) { const ArrayRef<mirror::Class*> types_array(types, count); - ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses(kRuntimeISA, types_array); + ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForClasses( + kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array); CreateJITCodeEntry(std::unique_ptr<const uint8_t[]>(elf_file.data()), elf_file.size()); } } diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index bc134fe27b..533dccf216 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -42,6 +42,9 @@ class JitCompiler { CompilerOptions* GetCompilerOptions() const { return compiler_options_.get(); } + CompilerDriver* GetCompilerDriver() const { + return compiler_driver_.get(); + } private: std::unique_ptr<CompilerOptions> compiler_options_; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 14fd1054c3..d22044aca3 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -176,6 +176,7 @@ class OatTest : public CommonCompilerTest { bool verify) { std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), file); elf_writer->Start(); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c60b02a227..50f5aba061 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -806,7 +806,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } } - if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) { + const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions(); + if (compiler_options.GenerateAnyDebugInfo()) { // Record debug information for this function if we are doing that. const uint32_t quick_code_start = quick_code_offset - writer_->oat_header_->GetExecutableOffset() - thumb_offset; @@ -817,6 +818,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { it.GetMethodAccessFlags(), it.GetMethodCodeItem(), deduped, + compiler_options.GetNativeDebuggable(), quick_code_start, quick_code_start + code_size, compiled_method}); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index f8a9a94e62..b95ece5a31 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -94,6 +94,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); + void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -1594,6 +1595,12 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 } +void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) { + uint32_t dex_pc = invoke->GetDexPc(); + HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier); +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { switch (instruction->GetIntrinsic()) { case Intrinsics::kStringEquals: @@ -1626,6 +1633,15 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kDoubleDoubleToLongBits: SimplifyFP2Int(instruction); break; + case Intrinsics::kUnsafeLoadFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny); + break; + case Intrinsics::kUnsafeStoreFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kAnyStore); + break; + case Intrinsics::kUnsafeFullFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kAnyAny); + break; default: break; } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 3ed0278871..5d4c4e2950 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -472,6 +472,24 @@ static Intrinsics GetIntrinsic(InlineMethod method) { break; } + // 1.8. + case kIntrinsicUnsafeGetAndAddInt: + return Intrinsics::kUnsafeGetAndAddInt; + case kIntrinsicUnsafeGetAndAddLong: + return Intrinsics::kUnsafeGetAndAddLong; + case kIntrinsicUnsafeGetAndSetInt: + return Intrinsics::kUnsafeGetAndSetInt; + case kIntrinsicUnsafeGetAndSetLong: + return Intrinsics::kUnsafeGetAndSetLong; + case kIntrinsicUnsafeGetAndSetObject: + return Intrinsics::kUnsafeGetAndSetObject; + case kIntrinsicUnsafeLoadFence: + return Intrinsics::kUnsafeLoadFence; + case kIntrinsicUnsafeStoreFence: + return Intrinsics::kUnsafeStoreFence; + case kIntrinsicUnsafeFullFence: + return Intrinsics::kUnsafeFullFence; + // Virtual cases. case kIntrinsicReferenceGetReferent: diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 0cec5ccfd3..3da82851a6 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -231,7 +231,10 @@ UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ UNREACHABLE_INTRINSIC(Arch, LongCompare) \ UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ -UNREACHABLE_INTRINSIC(Arch, LongSignum) +UNREACHABLE_INTRINSIC(Arch, LongSignum) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence) } // namespace art diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 69c970852d..c7d9a0d2ce 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -2002,6 +2002,13 @@ UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject) + UNREACHABLE_INTRINSICS(ARM) #undef __ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 934b42762e..8a444412f3 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1953,6 +1953,13 @@ UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject) + UNREACHABLE_INTRINSICS(ARM64) #undef __ diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index b8933e1684..dd9294d486 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -128,6 +128,14 @@ V(UnsafePutLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(UnsafePutLongOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(UnsafePutLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndAddInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndAddLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeLoadFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeStoreFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeFullFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) #endif // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 710df0a822..e159701a46 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1832,9 +1832,14 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh) UNIMPLEMENTED_INTRINSIC(MIPS, MathTan) UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) -UNREACHABLE_INTRINSICS(MIPS) +// 1.8. +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject) -#undef UNIMPLEMENTED_INTRINSIC +UNREACHABLE_INTRINSICS(MIPS) #undef __ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 617844bb18..0dc602f1ef 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1729,6 +1729,13 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(MIPS64, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject) + UNREACHABLE_INTRINSICS(MIPS64) #undef __ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 9a2dc4182d..4c802c0a1a 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2637,6 +2637,13 @@ UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) +// 1.8. +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject) + UNREACHABLE_INTRINSICS(X86) #undef __ diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 75204b4b49..41095cd485 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2715,6 +2715,13 @@ UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) +// 1.8. +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject) + UNREACHABLE_INTRINSICS(X86_64) #undef __ diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index b684cc697f..ecb690ff62 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -26,6 +26,7 @@ #include "base/arena_object.h" #include "base/stl_util.h" #include "dex/compiler_enums.h" +#include "dex_instruction-inl.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index c1b4d2403d..42f22afd80 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -913,7 +913,8 @@ bool OptimizingCompiler::JitCompile(Thread* self, return false; } - if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) { + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); CompiledMethod compiled_method( @@ -936,11 +937,15 @@ bool OptimizingCompiler::JitCompile(Thread* self, access_flags, code_item, false, // deduped. + compiler_options.GetNativeDebuggable(), code_address, code_address + code_allocator.GetSize(), &compiled_method }; - ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod(method_debug_info); + ArrayRef<const uint8_t> elf_file = debug::WriteDebugElfFileForMethod( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + method_debug_info); CreateJITCodeEntryForAddress(code_address, std::unique_ptr<const uint8_t[]>(elf_file.data()), elf_file.size()); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index ea190591e2..44e7fc9017 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1034,9 +1034,11 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); - key_value_store_->Put( - OatHeader::kExtractOnlyKey, - compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue); + if (compiler_options_->IsExtractOnly()) { + key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue); + } else if (UseProfileGuidedCompilation()) { + key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue); + } } // Parse the arguments from the command line. In case of an unrecognized option or impossible @@ -1891,13 +1893,6 @@ class Dex2Oat FINAL { return success; } - bool ShouldCompileBasedOnProfiles() const { - DCHECK(UseProfileGuidedCompilation()); - // If we are given a profile, compile only if we have some data in it. - return (profile_compilation_info_ != nullptr) && - (profile_compilation_info_->GetNumberOfMethods() != 0); - } - private: template <typename T> static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) { @@ -2105,8 +2100,10 @@ class Dex2Oat FINAL { elf_writers_.reserve(oat_files_.size()); oat_writers_.reserve(oat_files_.size()); for (const std::unique_ptr<File>& oat_file : oat_files_) { - elf_writers_.emplace_back( - CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get())); + elf_writers_.emplace_back(CreateElfWriterQuick(instruction_set_, + instruction_set_features_.get(), + compiler_options_.get(), + oat_file.get())); elf_writers_.back()->Start(); oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_)); } @@ -2593,16 +2590,11 @@ static int dex2oat(int argc, char** argv) { // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. dex2oat->ParseArgs(argc, argv); - // Process profile information and assess if we need to do a profile guided compilation. + // If needed, process profile information for profile guided compilation. // This operation involves I/O. if (dex2oat->UseProfileGuidedCompilation()) { - if (dex2oat->LoadProfile()) { - if (!dex2oat->ShouldCompileBasedOnProfiles()) { - LOG(INFO) << "Skipped compilation because of insignificant profile delta"; - return EXIT_SUCCESS; - } - } else { - LOG(WARNING) << "Failed to process profile files"; + if (!dex2oat->LoadProfile()) { + LOG(ERROR) << "Failed to process profile file"; return EXIT_FAILURE; } } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index c1875366e9..6c6c807588 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -98,6 +98,7 @@ const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* return ret; } +template <typename ElfTypes> class OatSymbolizer FINAL { public: OatSymbolizer(const OatFile* oat_file, const std::string& output_name) : @@ -115,11 +116,13 @@ class OatSymbolizer FINAL { bool Symbolize() { const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet(); + const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap( + isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap()); File* elf_file = OS::CreateEmptyFile(output_name_.c_str()); std::unique_ptr<BufferedOutputStream> output_stream( MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file))); - builder_.reset(new ElfBuilder<ElfTypes32>(isa, output_stream.get())); + builder_.reset(new ElfBuilder<ElfTypes>(isa, features, output_stream.get())); builder_->Start(); @@ -149,14 +152,14 @@ class OatSymbolizer FINAL { elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); builder_->WriteDynamicSection(); - Walk(&art::OatSymbolizer::RegisterForDedup); + Walk(&art::OatSymbolizer<ElfTypes>::RegisterForDedup); NormalizeState(); strtab->Start(); strtab->Write(""); // strtab should start with empty string. AddTrampolineSymbols(); - Walk(&art::OatSymbolizer::AddSymbol); + Walk(&art::OatSymbolizer<ElfTypes>::AddSymbol); strtab->End(); symtab->Start(); @@ -344,7 +347,7 @@ class OatSymbolizer FINAL { } const OatFile* oat_file_; - std::unique_ptr<ElfBuilder<ElfTypes32> > builder_; + std::unique_ptr<ElfBuilder<ElfTypes> > builder_; std::unordered_map<uint32_t, uint32_t> state_; const std::string output_name_; }; @@ -2542,8 +2545,17 @@ static int SymbolizeOat(const char* oat_filename, std::string& output_name) { return EXIT_FAILURE; } - OatSymbolizer oat_symbolizer(oat_file, output_name); - if (!oat_symbolizer.Symbolize()) { + bool result; + // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF + // files for 64-bit code in the past. + if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) { + OatSymbolizer<ElfTypes64> oat_symbolizer(oat_file, output_name); + result = oat_symbolizer.Symbolize(); + } else { + OatSymbolizer<ElfTypes32> oat_symbolizer(oat_file, output_name); + result = oat_symbolizer.Symbolize(); + } + if (!result) { fprintf(stderr, "Failed to symbolize\n"); return EXIT_FAILURE; } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 12d6d8f015..ebe89bbbd2 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -463,6 +463,12 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { interface_method->VisitRoots(visitor, pointer_size); } visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); + if (!IsNative()) { + ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); + if (profiling_info != nullptr) { + profiling_info->VisitRoots(visitor); + } + } } } diff --git a/runtime/art_method.cc b/runtime/art_method.cc index a60f31e52e..f97ad51568 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -377,7 +377,7 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { Runtime* runtime = Runtime::Current(); const void* existing_entry_point = GetEntryPointFromQuickCompiledCode(); - DCHECK(existing_entry_point != nullptr); + CHECK(existing_entry_point != nullptr) << PrettyMethod(this) << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 4672948f31..e4097dd3de 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -234,21 +234,34 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset); } -bool FdFile::WriteFully(const void* buffer, size_t byte_count) { +template <bool kUseOffset> +bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) { DCHECK(!read_only_mode_); - const char* ptr = static_cast<const char*>(buffer); moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); + DCHECK(kUseOffset || offset == 0u); + const char* ptr = static_cast<const char*>(buffer); while (byte_count > 0) { - ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); + ssize_t bytes_written = kUseOffset + ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset)) + : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count)); if (bytes_written == -1) { return false; } byte_count -= bytes_written; // Reduce the number of remaining bytes. ptr += bytes_written; // Move the buffer forward. + offset += static_cast<size_t>(bytes_written); } return true; } +bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) { + return WriteFullyGeneric<true>(buffer, byte_count, offset); +} + +bool FdFile::WriteFully(const void* buffer, size_t byte_count) { + return WriteFullyGeneric<false>(buffer, byte_count, 0u); +} + bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { DCHECK(!read_only_mode_); off_t off = static_cast<off_t>(offset); diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 8040afe9b7..16cd44f4ef 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -79,6 +79,7 @@ class FdFile : public RandomAccessFile { bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED; bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED; bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED; + bool PwriteFully(const void* buffer, size_t byte_count, size_t offset) WARN_UNUSED; // Copy data from another file. bool Copy(FdFile* input_file, int64_t offset, int64_t size); @@ -119,6 +120,9 @@ class FdFile : public RandomAccessFile { GuardState guard_state_; private: + template <bool kUseOffset> + bool WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset); + int fd_; std::string file_path_; bool auto_close_; diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc index ecf607c892..9bc87e5bb9 100644 --- a/runtime/base/unix_file/fd_file_test.cc +++ b/runtime/base/unix_file/fd_file_test.cc @@ -110,6 +110,34 @@ TEST_F(FdFileTest, ReadFullyWithOffset) { ASSERT_EQ(file.Close(), 0); } +TEST_F(FdFileTest, ReadWriteFullyWithOffset) { + // New scratch file, zero-length. + art::ScratchFile tmp; + FdFile file; + ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR)); + EXPECT_GE(file.Fd(), 0); + EXPECT_TRUE(file.IsOpened()); + + const char* test_string = "This is a test string"; + size_t length = strlen(test_string) + 1; + const size_t offset = 12; + std::unique_ptr<char[]> offset_read_string(new char[length]); + std::unique_ptr<char[]> read_string(new char[length]); + + // Write scratch data to file that we can read back into. + EXPECT_TRUE(file.PwriteFully(test_string, length, offset)); + ASSERT_EQ(file.Flush(), 0); + + // Test reading both the offsets. + EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset)); + EXPECT_STREQ(test_string, &offset_read_string[0]); + + EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u)); + EXPECT_NE(memcmp(&read_string[0], test_string, length), 0); + + ASSERT_EQ(file.Close(), 0); +} + TEST_F(FdFileTest, Copy) { art::ScratchFile src_tmp; FdFile src; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index a13a2e337c..d51a1f7ecc 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1207,217 +1207,222 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( Thread* const self = Thread::Current(); gc::Heap* const heap = Runtime::Current()->GetHeap(); const ImageHeader& header = space->GetImageHeader(); - // Add image classes into the class table for the class loader, and fixup the dex caches and - // class loader fields. - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* table = InsertClassTableForClassLoader(class_loader.Get()); - // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we - // rely on clobering the dex cache arrays in the image to forward to bss. - size_t num_dex_caches_with_bss_arrays = 0; - const size_t num_dex_caches = dex_caches->GetLength(); - for (size_t i = 0; i < num_dex_caches; i++) { - mirror::DexCache* const dex_cache = dex_caches->Get(i); - const DexFile* const dex_file = dex_cache->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); - if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - ++num_dex_caches_with_bss_arrays; - } - } - *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; - if (*out_forward_dex_cache_array) { - if (num_dex_caches_with_bss_arrays != num_dex_caches) { - // Reject application image since we cannot forward only some of the dex cache arrays. - // TODO: We could get around this by having a dedicated forwarding slot. It should be an - // uncommon case. - *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", - num_dex_caches_with_bss_arrays, - num_dex_caches); - return false; + { + // Add image classes into the class table for the class loader, and fixup the dex caches and + // class loader fields. + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* table = InsertClassTableForClassLoader(class_loader.Get()); + // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we + // rely on clobering the dex cache arrays in the image to forward to bss. + size_t num_dex_caches_with_bss_arrays = 0; + const size_t num_dex_caches = dex_caches->GetLength(); + for (size_t i = 0; i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { + ++num_dex_caches_with_bss_arrays; + } + } + *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0; + if (*out_forward_dex_cache_array) { + if (num_dex_caches_with_bss_arrays != num_dex_caches) { + // Reject application image since we cannot forward only some of the dex cache arrays. + // TODO: We could get around this by having a dedicated forwarding slot. It should be an + // uncommon case. + *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu", + num_dex_caches_with_bss_arrays, + num_dex_caches); + return false; + } } - } - // Only add the classes to the class loader after the points where we can return false. - for (size_t i = 0; i < num_dex_caches; i++) { - mirror::DexCache* const dex_cache = dex_caches->Get(i); - const DexFile* const dex_file = dex_cache->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); - if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { - // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and - // copy over the arrays. - DCHECK(dex_file != nullptr); - const size_t num_strings = dex_file->NumStringIds(); - const size_t num_types = dex_file->NumTypeIds(); - const size_t num_methods = dex_file->NumMethodIds(); - const size_t num_fields = dex_file->NumFieldIds(); - CHECK_EQ(num_strings, dex_cache->NumStrings()); - CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); - CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); - CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); - DexCacheArraysLayout layout(image_pointer_size_, dex_file); - uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); - // The space is not yet visible to the GC, we can avoid the read barriers and use std::copy_n. - if (num_strings != 0u) { - GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings(); - GcRoot<mirror::String>* const strings = - reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) { - DCHECK(strings[j].IsNull()); + // Only add the classes to the class loader after the points where we can return false. + for (size_t i = 0; i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) { + // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and + // copy over the arrays. + DCHECK(dex_file != nullptr); + const size_t num_strings = dex_file->NumStringIds(); + const size_t num_types = dex_file->NumTypeIds(); + const size_t num_methods = dex_file->NumMethodIds(); + const size_t num_fields = dex_file->NumFieldIds(); + CHECK_EQ(num_strings, dex_cache->NumStrings()); + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); + CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); + DexCacheArraysLayout layout(image_pointer_size_, dex_file); + uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); + // The space is not yet visible to the GC, we can avoid the read barriers and use + // std::copy_n. + if (num_strings != 0u) { + GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings(); + GcRoot<mirror::String>* const strings = + reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) { + DCHECK(strings[j].IsNull()); + } + std::copy_n(image_resolved_strings, num_strings, strings); + dex_cache->SetStrings(strings); } - std::copy_n(image_resolved_strings, num_strings, strings); - dex_cache->SetStrings(strings); - } - if (num_types != 0u) { - GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes(); - GcRoot<mirror::Class>* const types = - reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { - DCHECK(types[j].IsNull()); + if (num_types != 0u) { + GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes(); + GcRoot<mirror::Class>* const types = + reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { + DCHECK(types[j].IsNull()); + } + std::copy_n(image_resolved_types, num_types, types); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + // This leaves random garbage at the start of the dex cache array, but nobody should ever + // read from it again. + *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types; + dex_cache->SetResolvedTypes(types); } - std::copy_n(image_resolved_types, num_types, types); - // Store a pointer to the new location for fast ArtMethod patching without requiring map. - // This leaves random garbage at the start of the dex cache array, but nobody should ever - // read from it again. - *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types; - dex_cache->SetResolvedTypes(types); - } - if (num_methods != 0u) { - ArtMethod** const methods = reinterpret_cast<ArtMethod**>( - raw_arrays + layout.MethodsOffset()); - ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); - for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { - DCHECK(methods[j] == nullptr); + if (num_methods != 0u) { + ArtMethod** const methods = reinterpret_cast<ArtMethod**>( + raw_arrays + layout.MethodsOffset()); + ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); + for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { + DCHECK(methods[j] == nullptr); + } + std::copy_n(image_resolved_methods, num_methods, methods); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods; + dex_cache->SetResolvedMethods(methods); } - std::copy_n(image_resolved_methods, num_methods, methods); - // Store a pointer to the new location for fast ArtMethod patching without requiring map. - *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods; - dex_cache->SetResolvedMethods(methods); - } - if (num_fields != 0u) { - ArtField** const fields = reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset()); - for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) { - DCHECK(fields[j] == nullptr); + if (num_fields != 0u) { + ArtField** const fields = + reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) { + DCHECK(fields[j] == nullptr); + } + std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); + dex_cache->SetResolvedFields(fields); } - std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); - dex_cache->SetResolvedFields(fields); } - } - { - WriterMutexLock mu2(self, dex_lock_); - // Make sure to do this after we update the arrays since we store the resolved types array - // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the - // BSS. - mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self, - *dex_file, - /*allow_failure*/true); - CHECK(existing_dex_cache == nullptr); - StackHandleScope<1> hs3(self); - RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); - } - GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); - const size_t num_types = dex_cache->NumResolvedTypes(); - if (new_class_set == nullptr) { - for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { - // The image space is not yet added to the heap, avoid read barriers. - mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { - DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); - // Update the class loader from the one in the image class loader to the one that loaded - // the app image. - klass->SetClassLoader(class_loader.Get()); - // The resolved type could be from another dex cache, go through the dex cache just in - // case. May be null for array classes. - if (klass->GetDexCacheStrings() != nullptr) { - DCHECK(!klass->IsArrayClass()); - klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); - } - // If there are multiple dex caches, there may be the same class multiple times - // in different dex caches. Check for this since inserting will add duplicates - // otherwise. - if (num_dex_caches > 1) { - mirror::Class* existing = table->LookupByDescriptor(klass); - if (existing != nullptr) { - DCHECK_EQ(existing, klass) << PrettyClass(klass); + { + WriterMutexLock mu2(self, dex_lock_); + // Make sure to do this after we update the arrays since we store the resolved types array + // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the + // BSS. + mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self, + *dex_file, + /*allow_failure*/true); + CHECK(existing_dex_cache == nullptr); + StackHandleScope<1> hs3(self); + RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); + } + GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); + const size_t num_types = dex_cache->NumResolvedTypes(); + if (new_class_set == nullptr) { + for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + // Update the class loader from the one in the image class loader to the one that loaded + // the app image. + klass->SetClassLoader(class_loader.Get()); + // The resolved type could be from another dex cache, go through the dex cache just in + // case. May be null for array classes. + if (klass->GetDexCacheStrings() != nullptr) { + DCHECK(!klass->IsArrayClass()); + klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); + } + // If there are multiple dex caches, there may be the same class multiple times + // in different dex caches. Check for this since inserting will add duplicates + // otherwise. + if (num_dex_caches > 1) { + mirror::Class* existing = table->LookupByDescriptor(klass); + if (existing != nullptr) { + DCHECK_EQ(existing, klass) << PrettyClass(klass); + } else { + table->Insert(klass); + } } else { table->Insert(klass); } - } else { - table->Insert(klass); - } - // Double checked VLOG to avoid overhead. - if (VLOG_IS_ON(image)) { - VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus(); - if (!klass->IsArrayClass()) { - VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation(); - } - VLOG(image) << "Direct methods"; - for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { - VLOG(image) << PrettyMethod(&m); - } - VLOG(image) << "Virtual methods"; - for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { - VLOG(image) << PrettyMethod(&m); + // Double checked VLOG to avoid overhead. + if (VLOG_IS_ON(image)) { + VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus(); + if (!klass->IsArrayClass()) { + VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation(); + } + VLOG(image) << "Direct methods"; + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } + VLOG(image) << "Virtual methods"; + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } } } } } - } - if (kIsDebugBuild) { - for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { - // The image space is not yet added to the heap, avoid read barriers. - mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { - DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); - if (kIsDebugBuild) { - if (new_class_set != nullptr) { - auto it = new_class_set->Find(GcRoot<mirror::Class>(klass)); - DCHECK(it != new_class_set->end()); - DCHECK_EQ(it->Read(), klass); - mirror::Class* super_class = klass->GetSuperClass(); - if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { - auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class)); - DCHECK(it2 != new_class_set->end()); - DCHECK_EQ(it2->Read(), super_class); - } - } else { - DCHECK_EQ(table->LookupByDescriptor(klass), klass); - mirror::Class* super_class = klass->GetSuperClass(); - if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { - CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + if (kIsDebugBuild) { + for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + if (kIsDebugBuild) { + if (new_class_set != nullptr) { + auto it = new_class_set->Find(GcRoot<mirror::Class>(klass)); + DCHECK(it != new_class_set->end()); + DCHECK_EQ(it->Read(), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class)); + DCHECK(it2 != new_class_set->end()); + DCHECK_EQ(it2->Read(), super_class); + } + } else { + DCHECK_EQ(table->LookupByDescriptor(klass), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + } } } - } - if (kIsDebugBuild) { - for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { - const void* code = m.GetEntryPointFromQuickCompiledCode(); - const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; - if (!IsQuickResolutionStub(code) && - !IsQuickGenericJniStub(code) && - !IsQuickToInterpreterBridge(code) && - !m.IsNative()) { - DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + if (kIsDebugBuild) { + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } } - } - for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { - const void* code = m.GetEntryPointFromQuickCompiledCode(); - const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; - if (!IsQuickResolutionStub(code) && - !IsQuickGenericJniStub(code) && - !IsQuickToInterpreterBridge(code) && - !m.IsNative()) { - DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } } } } } } } - } - if (*out_forward_dex_cache_array) { - FixupArtMethodArrayVisitor visitor(header); - header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( - &visitor, - space->Begin(), - sizeof(void*)); - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + if (*out_forward_dex_cache_array) { + ScopedTrace timing("Fixup ArtMethod dex cache arrays"); + FixupArtMethodArrayVisitor visitor(header); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor, + space->Begin(), + sizeof(void*)); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + } } return true; } @@ -7709,7 +7714,10 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo } ++num_resolved; DCHECK(!klass->IsProxyClass()); - DCHECK(klass->IsResolved()); + if (!klass->IsResolved()) { + DCHECK(klass->IsErroneous()); + continue; + } mirror::DexCache* klass_dex_cache = klass->GetDexCache(); if (klass_dex_cache == dex_cache) { const size_t class_def_idx = klass->GetDexClassDefIndex(); diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 3df9101613..729957f318 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -406,6 +406,7 @@ void CommonRuntimeTestImpl::TearDown() { int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); ASSERT_EQ(0, rmdir_cache_result); TearDownAndroidData(android_data_, true); + dalvik_cache_.clear(); // icu4c has a fixed 10-element array "gCommonICUDataArray". // If we run > 10 tests, we fill that array and u_setCommonData fails. diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 7727b2da18..6beb60608c 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -131,7 +131,7 @@ void MarkCompact::ProcessReferences(Thread* self) { class BitmapSetSlowPathVisitor { public: - void operator()(const mirror::Object* obj) const { + void operator()(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) { // Marking a large object, make sure its aligned as a sanity check. if (!IsAligned<kPageSize>(obj)) { Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 01dff190f0..2e5b599940 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1547,7 +1547,6 @@ std::string Heap::DumpSpaces() const { } void Heap::DumpSpaces(std::ostream& stream) const { - ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap(); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 889069d8ae..e0a53a0cc8 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -651,8 +651,8 @@ class Heap { } } - std::string DumpSpaces() const WARN_UNUSED; - void DumpSpaces(std::ostream& stream) const; + void DumpSpaces(std::ostream& stream) const SHARED_REQUIRES(Locks::mutator_lock_); + std::string DumpSpaces() const SHARED_REQUIRES(Locks::mutator_lock_); // Dump object should only be used by the signal handler. void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index a4e558728e..9ecd391e4d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1535,50 +1535,31 @@ void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_n // images[0] is f/c/d/e.art // ---------------------------------------------- // images[1] is g/h/i/j.art -> /a/b/h/i/j.art - - // Derive pattern. - std::vector<std::string> left; - Split(input_image_file_name, '/', &left); - std::vector<std::string> right; - Split(images[0], '/', &right); - - size_t common = 1; - while (common < left.size() && common < right.size()) { - if (left[left.size() - common - 1] != right[right.size() - common - 1]) { - break; - } - common++; - } - - std::vector<std::string> prefix_vector(left.begin(), left.end() - common); - std::string common_prefix = Join(prefix_vector, '/'); - if (!common_prefix.empty() && common_prefix[0] != '/' && input_image_file_name[0] == '/') { - common_prefix = "/" + common_prefix; - } + const std::string& first_image = images[0]; + // Length of common suffix. + size_t common = 0; + while (common < input_image_file_name.size() && + common < first_image.size() && + *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) { + ++common; + } + // We want to replace the prefix of the input image with the prefix of the boot class path. + // This handles the case where the image file contains @ separators. + // Example image_file_name is oats/system@framework@boot.art + // images[0] is .../arm/boot.art + // means that the image name prefix will be oats/system@framework@ + // so that the other images are openable. + const size_t old_prefix_length = first_image.size() - common; + const std::string new_prefix = input_image_file_name.substr( + 0, + input_image_file_name.size() - common); // Apply pattern to images[1] .. images[n]. for (size_t i = 1; i < images.size(); ++i) { - std::string image = images[i]; - - size_t rslash = std::string::npos; - for (size_t j = 0; j < common; ++j) { - if (rslash != std::string::npos) { - rslash--; - } - - rslash = image.rfind('/', rslash); - if (rslash == std::string::npos) { - rslash = 0; - } - if (rslash == 0) { - break; - } - } - std::string image_part = image.substr(rslash); - - std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") + - image_part; - image_file_names->push_back(new_image); + const std::string& image = images[i]; + CHECK_GT(image.length(), old_prefix_length); + std::string suffix = image.substr(old_prefix_length); + image_file_names->push_back(new_prefix + suffix); } } diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index e70fe215ab..010f677885 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -27,6 +27,7 @@ #include "base/stl_util.h" #include "image.h" #include "os.h" +#include "scoped_thread_state_change.h" #include "space-inl.h" #include "thread-inl.h" @@ -190,6 +191,7 @@ size_t LargeObjectMapSpace::Free(Thread* self, mirror::Object* ptr) { MutexLock mu(self, lock_); auto it = large_objects_.find(ptr); if (UNLIKELY(it == large_objects_.end())) { + ScopedObjectAccess soa(self); Runtime::Current()->GetHeap()->DumpSpaces(LOG(INTERNAL_FATAL)); LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live"; } diff --git a/runtime/interpreter/mterp/mips/binop.S b/runtime/interpreter/mterp/mips/binop.S index ce09da453a..66627e2719 100644 --- a/runtime/interpreter/mterp/mips/binop.S +++ b/runtime/interpreter/mterp/mips/binop.S @@ -7,8 +7,8 @@ * * If "chkzero" is set to 1, we perform a divide-by-zero check on * vCC (a1). Useful for integer division and modulus. Note that we - * *don't* check for (INT_MIN / -1) here, because the ARM math lib - * handles it correctly. + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. * * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, * xor-int, shl-int, shr-int, ushr-int diff --git a/runtime/interpreter/mterp/mips64/bincmp.S b/runtime/interpreter/mterp/mips64/bincmp.S index d39c9009b7..aa5e74b3de 100644 --- a/runtime/interpreter/mterp/mips64/bincmp.S +++ b/runtime/interpreter/mterp/mips64/bincmp.S @@ -6,27 +6,27 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - b${condition}c a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S index 1a2e22b672..14d5fe01f5 100644 --- a/runtime/interpreter/mterp/mips64/footer.S +++ b/runtime/interpreter/mterp/mips64/footer.S @@ -49,6 +49,7 @@ MterpPossibleException: * */ .extern MterpHandleException + .extern MterpShouldSwitchInterpreters MterpException: move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -59,8 +60,11 @@ MterpException: REFRESH_IBASE daddu rPC, a0, CODEITEM_INSNS_OFFSET dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr - sd rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -81,10 +85,24 @@ check1: EXPORT_PC move a0, rSELF jal MterpSuspendCheck # (self) + bnezc v0, MterpFallback # Something in the environment changed, switch interpreters GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST # rINST contains offset + jal MterpLogOSR +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* * Bail out to reference interpreter. */ .extern MterpLogFallback diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S index 4c3ca9efff..dd0fbe0057 100644 --- a/runtime/interpreter/mterp/mips64/header.S +++ b/runtime/interpreter/mterp/mips64/header.S @@ -82,14 +82,7 @@ The following registers have fixed assignments: #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) -/* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - +#define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 /* diff --git a/runtime/interpreter/mterp/mips64/invoke.S b/runtime/interpreter/mterp/mips64/invoke.S index 4ae4fb1c1d..be647b618b 100644 --- a/runtime/interpreter/mterp/mips64/invoke.S +++ b/runtime/interpreter/mterp/mips64/invoke.S @@ -5,6 +5,7 @@ /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern $helper + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -13,5 +14,7 @@ jal $helper beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 diff --git a/runtime/interpreter/mterp/mips64/op_goto.S b/runtime/interpreter/mterp/mips64/op_goto.S index f2df3e4112..7c7d0ecf5a 100644 --- a/runtime/interpreter/mterp/mips64/op_goto.S +++ b/runtime/interpreter/mterp/mips64/op_goto.S @@ -5,19 +5,21 @@ * double to get a byte offset. */ /* goto +AA */ - srl a0, rINST, 8 - seb a0, a0 # a0 <- sign-extended AA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + srl rINST, rINST, 8 + seb rINST, rINST # rINST <- offset (sign-extended AA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_16.S b/runtime/interpreter/mterp/mips64/op_goto_16.S index cbf8cf2994..566e3a78f0 100644 --- a/runtime/interpreter/mterp/mips64/op_goto_16.S +++ b/runtime/interpreter/mterp/mips64/op_goto_16.S @@ -5,18 +5,20 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ - lh a0, 2(rPC) # a0 <- sign-extended AAAA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_32.S b/runtime/interpreter/mterp/mips64/op_goto_32.S index 4a1feac1ac..b260083ae8 100644 --- a/runtime/interpreter/mterp/mips64/op_goto_32.S +++ b/runtime/interpreter/mterp/mips64/op_goto_32.S @@ -8,20 +8,22 @@ * our "backward branch" test must be "<=0" instead of "<0". */ /* goto/32 +AAAAAAAA */ - lh a0, 2(rPC) # a0 <- aaaa (low) + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- aaaa (low) lh a1, 4(rPC) # a1 <- AAAA (high) - ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgtz a0, 1f # AA * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez a0, MterpCheckSuspendAndContinue + ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_packed_switch.S b/runtime/interpreter/mterp/mips64/op_packed_switch.S index cdbdf75321..2c6eb2f3ca 100644 --- a/runtime/interpreter/mterp/mips64/op_packed_switch.S +++ b/runtime/interpreter/mterp/mips64/op_packed_switch.S @@ -10,6 +10,7 @@ */ /* op vAA, +BBBBBBBB */ .extern $func + .extern MterpProfileBranch lh a0, 2(rPC) # a0 <- bbbb (lo) lh a1, 4(rPC) # a1 <- BBBB (hi) srl a3, rINST, 8 # a3 <- AA @@ -17,15 +18,19 @@ GET_VREG a1, a3 # a1 <- vAA dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 jal $func # v0 <- code-unit branch offset - dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 - FETCH_INST # load rINST -#if MTERP_SUSPEND - bgtz v0, 1f # offset * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez v0, MterpCheckSuspendAndContinue + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/zcmp.S b/runtime/interpreter/mterp/mips64/zcmp.S index d7ad894485..0e0477fadf 100644 --- a/runtime/interpreter/mterp/mips64/zcmp.S +++ b/runtime/interpreter/mterp/mips64/zcmp.S @@ -6,25 +6,25 @@ * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - b${condition}zc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index ca727f47be..10b19c5f4f 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -147,16 +147,7 @@ extern "C" bool MterpShouldSwitchInterpreters() SHARED_REQUIRES(Locks::mutator_lock_) { const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - bool unhandled_instrumentation; - // TODO: enable for other targets after more extensive testing. - if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || - (kRuntimeISA == kX86_64) || (kRuntimeISA == kX86) || - (kRuntimeISA == kMips)) { - unhandled_instrumentation = instrumentation->NonJitProfilingActive(); - } else { - unhandled_instrumentation = instrumentation->IsActive(); - } - return unhandled_instrumentation || Dbg::IsDebuggerActive(); + return instrumentation->NonJitProfilingActive() || Dbg::IsDebuggerActive(); } diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 7cef823fff..a17252b2f8 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -89,14 +89,7 @@ The following registers have fixed assignments: #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) -/* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - +#define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 /* @@ -1107,20 +1100,22 @@ artMterpAsmInstructionStart = .L_op_nop * double to get a byte offset. */ /* goto +AA */ - srl a0, rINST, 8 - seb a0, a0 # a0 <- sign-extended AA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + srl rINST, rINST, 8 + seb rINST, rINST # rINST <- offset (sign-extended AA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1135,19 +1130,21 @@ artMterpAsmInstructionStart = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ - lh a0, 2(rPC) # a0 <- sign-extended AAAA - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a0, 1f # AA * 2 >= 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a0, MterpCheckSuspendAndContinue + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1165,21 +1162,23 @@ artMterpAsmInstructionStart = .L_op_nop * our "backward branch" test must be "<=0" instead of "<0". */ /* goto/32 +AAAAAAAA */ - lh a0, 2(rPC) # a0 <- aaaa (low) + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- aaaa (low) lh a1, 4(rPC) # a1 <- AAAA (high) - ins a0, a1, 16, 16 # a0 = sign-extended AAAAaaaa - dlsa rPC, a0, rPC, 1 # rPC <- rPC + AAAAAAAA * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgtz a0, 1f # AA * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez a0, MterpCheckSuspendAndContinue + ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1198,6 +1197,7 @@ artMterpAsmInstructionStart = .L_op_nop */ /* op vAA, +BBBBBBBB */ .extern MterpDoPackedSwitch + .extern MterpProfileBranch lh a0, 2(rPC) # a0 <- bbbb (lo) lh a1, 4(rPC) # a1 <- BBBB (hi) srl a3, rINST, 8 # a3 <- AA @@ -1205,16 +1205,20 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG a1, a3 # a1 <- vAA dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 jal MterpDoPackedSwitch # v0 <- code-unit branch offset - dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 - FETCH_INST # load rINST -#if MTERP_SUSPEND - bgtz v0, 1f # offset * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez v0, MterpCheckSuspendAndContinue + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1234,6 +1238,7 @@ artMterpAsmInstructionStart = .L_op_nop */ /* op vAA, +BBBBBBBB */ .extern MterpDoSparseSwitch + .extern MterpProfileBranch lh a0, 2(rPC) # a0 <- bbbb (lo) lh a1, 4(rPC) # a1 <- BBBB (hi) srl a3, rINST, 8 # a3 <- AA @@ -1241,16 +1246,20 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG a1, a3 # a1 <- vAA dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 jal MterpDoSparseSwitch # v0 <- code-unit branch offset - dlsa rPC, v0, rPC, 1 # rPC <- rPC + offset * 2 - FETCH_INST # load rINST -#if MTERP_SUSPEND - bgtz v0, 1f # offset * 2 > 0 => no suspend check - REFRESH_IBASE -1: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - blez v0, MterpCheckSuspendAndContinue + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1438,28 +1447,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - beqc a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1477,28 +1486,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bnec a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1516,28 +1525,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bltc a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1555,28 +1564,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bgec a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1594,28 +1603,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - bgtc a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1633,28 +1642,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ - lh a4, 2(rPC) # a4 <- sign-extended CCCC + .extern MterpProfileBranch ext a2, rINST, 8, 4 # a2 <- A ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) GET_VREG a0, a2 # a0 <- vA GET_VREG a1, a3 # a1 <- vB - blec a0, a1, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + CCCC * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # CCCC * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1672,26 +1681,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - beqzc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1709,26 +1718,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bnezc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1746,26 +1755,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bltzc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1783,26 +1792,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bgezc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1820,26 +1829,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - bgtzc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -1857,26 +1866,26 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ - lh a4, 2(rPC) # a4 <- sign-extended BBBB + .extern MterpProfileBranch srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) GET_VREG a0, a2 # a0 <- vAA - blezc a0, 1f - li a4, 2 # offset if branch not taken + li rINST, 2 # offset if branch not taken 1: - - dlsa rPC, a4, rPC, 1 # rPC <- rPC + BBBB * 2 - FETCH_INST # load rINST - -#if MTERP_SUSPEND - bgez a4, 2f # BBBB * 2 >= 0 => no suspend check - REFRESH_IBASE -2: -#else - lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue - bltz a4, MterpCheckSuspendAndContinue +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST #endif - + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction @@ -3166,6 +3175,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtual + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3174,6 +3184,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtual beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3196,6 +3208,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeSuper + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3204,6 +3217,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeSuper beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3226,6 +3241,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeDirect + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3234,6 +3250,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeDirect beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3249,6 +3267,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeStatic + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3257,6 +3276,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeStatic beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3272,6 +3293,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeInterface + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3280,6 +3302,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeInterface beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3316,6 +3340,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtualRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3324,6 +3349,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtualRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3339,6 +3366,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeSuperRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3347,6 +3375,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeSuperRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3362,6 +3392,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeDirectRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3370,6 +3401,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeDirectRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3385,6 +3418,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeStaticRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3393,6 +3427,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeStaticRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -3408,6 +3444,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeInterfaceRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -3416,6 +3453,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeInterfaceRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -6962,6 +7001,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtualQuick + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -6970,6 +7010,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtualQuick beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -6985,6 +7027,7 @@ artMterpAsmInstructionStart = .L_op_nop /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ .extern MterpInvokeVirtualQuickRange + .extern MterpShouldSwitchInterpreters EXPORT_PC move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -6993,6 +7036,8 @@ artMterpAsmInstructionStart = .L_op_nop jal MterpInvokeVirtualQuickRange beqzc v0, MterpException FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -12256,6 +12301,7 @@ MterpPossibleException: * */ .extern MterpHandleException + .extern MterpShouldSwitchInterpreters MterpException: move a0, rSELF daddu a1, rFP, OFF_FP_SHADOWFRAME @@ -12266,8 +12312,11 @@ MterpException: REFRESH_IBASE daddu rPC, a0, CODEITEM_INSNS_OFFSET dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr - sd rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE v0 GOTO_OPCODE v0 @@ -12288,10 +12337,24 @@ check1: EXPORT_PC move a0, rSELF jal MterpSuspendCheck # (self) + bnezc v0, MterpFallback # Something in the environment changed, switch interpreters GET_INST_OPCODE v0 # extract opcode from rINST GOTO_OPCODE v0 # jump to next instruction /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST # rINST contains offset + jal MterpLogOSR +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* * Bail out to reference interpreter. */ .extern MterpLogFallback diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index a3b99e3dda..7dbd89cedb 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -188,9 +188,11 @@ void Jit::DeleteThreadPool() { } void Jit::StartProfileSaver(const std::string& filename, - const std::vector<std::string>& code_paths) { + const std::vector<std::string>& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_dir) { if (save_profiling_info_) { - ProfileSaver::Start(filename, code_cache_.get(), code_paths); + ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir); } } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 3f54192d9f..ee416d8772 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -70,7 +70,17 @@ class Jit { return instrumentation_cache_.get(); } - void StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths); + // Starts the profile saver if the config options allow profile recording. + // The profile will be stored in the specified `filename` and will contain + // information collected from the given `code_paths` (a set of dex locations). + // The `foreign_dex_profile_path` is the path where the saver will put the + // profile markers for loaded dex files which are not owned by the application. + // The `app_dir` is the application directory and is used to decide which + // dex files belong to the application. + void StartProfileSaver(const std::string& filename, + const std::vector<std::string>& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_dir); void StopProfileSaver(); void DumpForSigQuit(std::ostream& os) { diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 4f87e5bab5..050bb68336 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -169,12 +169,16 @@ bool JitCodeCache::ContainsMethod(ArtMethod* method) { return false; } -class ScopedCodeCacheWrite { +class ScopedCodeCacheWrite : ScopedTrace { public: - explicit ScopedCodeCacheWrite(MemMap* code_map) : code_map_(code_map) { + explicit ScopedCodeCacheWrite(MemMap* code_map) + : ScopedTrace("ScopedCodeCacheWrite"), + code_map_(code_map) { + ScopedTrace trace("mprotect all"); CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtAll); } ~ScopedCodeCacheWrite() { + ScopedTrace trace("mprotect code"); CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode); } private: diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index bd581570ed..6fe17dbe15 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -16,6 +16,10 @@ #include "profile_saver.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include "art_method-inl.h" #include "base/systrace.h" #include "scoped_thread_state_change.h" @@ -43,14 +47,31 @@ pthread_t ProfileSaver::profiler_pthread_ = 0U; ProfileSaver::ProfileSaver(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector<std::string>& code_paths) + const std::vector<std::string>& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) : jit_code_cache_(jit_code_cache), + foreign_dex_profile_path_(foreign_dex_profile_path), code_cache_last_update_time_ns_(0), shutting_down_(false), first_profile_(true), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_) { AddTrackedLocations(output_filename, code_paths); + app_data_dir_ = ""; + if (!app_data_dir.empty()) { + // The application directory is used to determine which dex files are owned by app. + // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we + // don't have control over how the dex files are actually loaded (symlink or canonical path), + // store it's canonical form to be sure we use the same base when comparing. + UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr)); + if (app_data_dir_real_path != nullptr) { + app_data_dir_.assign(app_data_dir_real_path.get()); + } else { + LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_ + << ". The app dir will not be used to determine which dex files belong to the app"; + } + } } void ProfileSaver::Run() { @@ -164,7 +185,9 @@ void* ProfileSaver::RunProfileSaverThread(void* arg) { void ProfileSaver::Start(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector<std::string>& code_paths) { + const std::vector<std::string>& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) { DCHECK(Runtime::Current()->UseJit()); DCHECK(!output_filename.empty()); DCHECK(jit_code_cache != nullptr); @@ -183,7 +206,11 @@ void ProfileSaver::Start(const std::string& output_filename, VLOG(profiler) << "Starting profile saver using output file: " << output_filename << ". Tracking: " << Join(code_paths, ':'); - instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths); + instance_ = new ProfileSaver(output_filename, + jit_code_cache, + code_paths, + foreign_dex_profile_path, + app_data_dir); // Create a new thread which does the saving. CHECK_PTHREAD_CALL( @@ -250,4 +277,97 @@ void ProfileSaver::AddTrackedLocations(const std::string& output_filename, } } +void ProfileSaver::NotifyDexUse(const std::string& dex_location) { + std::set<std::string> app_code_paths; + std::string foreign_dex_profile_path; + std::string app_data_dir; + { + MutexLock mu(Thread::Current(), *Locks::profiler_lock_); + DCHECK(instance_ != nullptr); + // Make a copy so that we don't hold the lock while doing I/O. + for (const auto& it : instance_->tracked_dex_base_locations_) { + app_code_paths.insert(it.second.begin(), it.second.end()); + } + foreign_dex_profile_path = instance_->foreign_dex_profile_path_; + app_data_dir = instance_->app_data_dir_; + } + + MaybeRecordDexUseInternal(dex_location, + app_code_paths, + foreign_dex_profile_path, + app_data_dir); +} + +void ProfileSaver::MaybeRecordDexUseInternal( + const std::string& dex_location, + const std::set<std::string>& app_code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) { + if (dex_location.empty()) { + LOG(WARNING) << "Asked to record foreign dex use with an empty dex location."; + return; + } + if (foreign_dex_profile_path.empty()) { + LOG(WARNING) << "Asked to record foreign dex use without a valid profile path "; + return; + } + + UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr)); + if (dex_location_real_path == nullptr) { + PLOG(WARNING) << "Could not get realpath for " << dex_location; + } + std::string dex_location_real_path_str((dex_location_real_path == nullptr) + ? dex_location.c_str() + : dex_location_real_path.get()); + + if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) { + // The dex location is under the application folder. Nothing to record. + return; + } + + if (app_code_paths.find(dex_location) != app_code_paths.end()) { + // The dex location belongs to the application code paths. Nothing to record. + return; + } + // Do another round of checks with the real paths. + // Note that we could cache all the real locations in the saver (since it's an expensive + // operation). However we expect that app_code_paths is small (usually 1 element), and + // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise + // to save some bytes of memory usage. + for (const auto& app_code_location : app_code_paths) { + UniqueCPtr<const char[]> real_app_code_location(realpath(app_code_location.c_str(), nullptr)); + if (real_app_code_location == nullptr) { + PLOG(WARNING) << "Could not get realpath for " << app_code_location; + } + std::string real_app_code_location_str((real_app_code_location == nullptr) + ? app_code_location.c_str() + : real_app_code_location.get()); + if (real_app_code_location_str == dex_location_real_path_str) { + // The dex location belongs to the application code paths. Nothing to record. + return; + } + } + + // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this + // into account when deciding how to optimize the loaded dex file. + // The expected flag name is the canonical path of the apk where '/' is substituted to '@'. + // (it needs to be kept in sync with + // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java) + std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@'); + std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str; + // No need to give any sort of access to flag_path. The system has enough permissions + // to test for its existence. + int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(), O_CREAT | O_EXCL, 0)); + if (fd != -1) { + if (close(fd) != 0) { + PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path; + } + } else { + if (errno != EEXIST) { + // Another app could have already created the file. + PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path; + } + } +} + } // namespace art diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index 21017c1acd..e7eab95f3d 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -30,7 +30,9 @@ class ProfileSaver { // If the saver is already running it adds (output_filename, code_paths) to its tracked locations. static void Start(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector<std::string>& code_paths) + const std::vector<std::string>& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir) REQUIRES(!Locks::profiler_lock_, !wait_lock_); // Stops the profile saver thread. @@ -42,10 +44,14 @@ class ProfileSaver { // Returns true if the profile saver is started. static bool IsStarted() REQUIRES(!Locks::profiler_lock_); + static void NotifyDexUse(const std::string& dex_location); + private: ProfileSaver(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, - const std::vector<std::string>& code_paths); + const std::vector<std::string>& code_paths, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir); // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock. static void* RunProfileSaverThread(void* arg) @@ -64,6 +70,12 @@ class ProfileSaver { const std::vector<std::string>& code_paths) REQUIRES(Locks::profiler_lock_); + static void MaybeRecordDexUseInternal( + const std::string& dex_location, + const std::set<std::string>& tracked_locations, + const std::string& foreign_dex_profile_path, + const std::string& app_data_dir); + // The only instance of the saver. static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_); // Profile saver thread. @@ -72,6 +84,8 @@ class ProfileSaver { jit::JitCodeCache* jit_code_cache_; SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_ GUARDED_BY(Locks::profiler_lock_); + std::string foreign_dex_profile_path_; + std::string app_data_dir_; uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); bool first_profile_ = true; diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 11156c6229..421641ce39 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -590,7 +590,19 @@ void MemMap::MadviseDontNeedAndZero() { } bool MemMap::Sync() { - return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; + bool result; + if (redzone_size_ != 0) { + // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing + // it to msync() as it only accepts page-aligned base address, and exclude the higher-end + // noaccess protection from the msync range. b/27552451. + uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_); + MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); + result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; + MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin); + } else { + result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; + } + return result; } bool MemMap::Protect(int prot) { diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 103a8b79ef..19584edf7f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -29,7 +29,6 @@ #include "dex_cache.h" #include "dex_file.h" #include "gc/heap-inl.h" -#include "jit/profiling_info.h" #include "iftable.h" #include "object_array-inl.h" #include "read_barrier-inl.h" @@ -940,12 +939,6 @@ void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { } for (ArtMethod& method : GetMethods(pointer_size)) { method.VisitRoots(visitor, pointer_size); - if (method.GetDeclaringClassUnchecked() != nullptr && !method.IsNative()) { - ProfilingInfo* profiling_info = method.GetProfilingInfo(pointer_size); - if (profiling_info != nullptr) { - profiling_info->VisitRoots(visitor); - } - } } } diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 6643ac2231..f1e0fa7b57 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -347,15 +347,14 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie static jint GetDexOptNeeded(JNIEnv* env, const char* filename, - const char* pkgname, const char* instruction_set, - const jboolean defer) { + const int target_compilation_type_mask) { if ((filename == nullptr) || !OS::FileExists(filename)) { LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist"; ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException")); const char* message = (filename == nullptr) ? "<empty file name>" : filename; env->ThrowNew(fnfe.get(), message); - return OatFileAssistant::kNoDexOptNeeded; + return -1; } const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set); @@ -363,73 +362,52 @@ static jint GetDexOptNeeded(JNIEnv* env, ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException")); std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set)); env->ThrowNew(iae.get(), message.c_str()); - return 0; + return -1; } // TODO: Verify the dex location is well formed, and throw an IOException if // not? - - OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname); + OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask, + target_instruction_set, false); // Always treat elements of the bootclasspath as up-to-date. if (oat_file_assistant.IsInBootClassPath()) { return OatFileAssistant::kNoDexOptNeeded; } - // TODO: Checking the profile should probably be done in the GetStatus() - // function. We have it here because GetStatus() should not be copying - // profile files. But who should be copying profile files? - if (oat_file_assistant.OdexFileIsOutOfDate()) { - // Needs recompile if profile has changed significantly. - if (Runtime::Current()->GetProfilerOptions().IsEnabled()) { - if (oat_file_assistant.IsProfileChangeSignificant()) { - if (!defer) { - oat_file_assistant.CopyProfileFile(); - } - return OatFileAssistant::kDex2OatNeeded; - } else if (oat_file_assistant.ProfileExists() - && !oat_file_assistant.OldProfileExists()) { - if (!defer) { - oat_file_assistant.CopyProfileFile(); - } - } - } - } - return oat_file_assistant.GetDexOptNeeded(); } static jint DexFile_getDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename, - jstring javaPkgname, jstring javaInstructionSet, - jboolean defer) { + jint javaTargetCompilationTypeMask) { ScopedUtfChars filename(env, javaFilename); if (env->ExceptionCheck()) { - return 0; + return -1; } - NullableScopedUtfChars pkgname(env, javaPkgname); - ScopedUtfChars instruction_set(env, javaInstructionSet); if (env->ExceptionCheck()) { - return 0; + return -1; } return GetDexOptNeeded(env, filename.c_str(), - pkgname.c_str(), instruction_set.c_str(), - defer); + javaTargetCompilationTypeMask); } -// public API, null pkgname +// public API static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) { const char* instruction_set = GetInstructionSetString(kRuntimeISA); ScopedUtfChars filename(env, javaFilename); - jint status = GetDexOptNeeded(env, filename.c_str(), nullptr /* pkgname */, - instruction_set, false /* defer */); + jint status = GetDexOptNeeded( + env, + filename.c_str(), + instruction_set, + OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation); return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE; } @@ -445,7 +423,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"), NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"), NATIVE_METHOD(DexFile, getDexOptNeeded, - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"), + "(Ljava/lang/String;Ljava/lang/String;I)I"), NATIVE_METHOD(DexFile, openDexFileNative, "(Ljava/lang/String;" "Ljava/lang/String;" diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index da4a891ff5..f6b2f21515 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -566,8 +566,9 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { static void VMRuntime_registerAppInfo(JNIEnv* env, jclass clazz ATTRIBUTE_UNUSED, jstring profile_file, - jstring app_dir ATTRIBUTE_UNUSED, // TODO: remove argument - jobjectArray code_paths) { + jstring app_dir, + jobjectArray code_paths, + jstring foreign_dex_profile_path) { std::vector<std::string> code_paths_vec; int code_paths_length = env->GetArrayLength(code_paths); for (int i = 0; i < code_paths_length; i++) { @@ -581,7 +582,22 @@ static void VMRuntime_registerAppInfo(JNIEnv* env, std::string profile_file_str(raw_profile_file); env->ReleaseStringUTFChars(profile_file, raw_profile_file); - Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str); + std::string foreign_dex_profile_path_str = ""; + if (foreign_dex_profile_path != nullptr) { + const char* raw_foreign_dex_profile_path = + env->GetStringUTFChars(foreign_dex_profile_path, nullptr); + foreign_dex_profile_path_str.assign(raw_foreign_dex_profile_path); + env->ReleaseStringUTFChars(foreign_dex_profile_path, raw_foreign_dex_profile_path); + } + + const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr); + std::string app_dir_str(raw_app_dir); + env->ReleaseStringUTFChars(app_dir, raw_app_dir); + + Runtime::Current()->RegisterAppInfo(code_paths_vec, + profile_file_str, + foreign_dex_profile_path_str, + app_dir_str); } static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) { @@ -638,7 +654,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"), NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"), NATIVE_METHOD(VMRuntime, registerAppInfo, - "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"), + "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"), NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"), NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"), }; diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 6ffd476edf..858849f980 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -26,6 +26,7 @@ #include <unistd.h> #include <stdlib.h> #include <string.h> +#include <atomic> namespace art { @@ -473,6 +474,18 @@ static void Unsafe_putDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset obj->SetField64<false>(MemberOffset(offset), conv.converted); } +static void Unsafe_loadFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_acquire); +} + +static void Unsafe_storeFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_release); +} + +static void Unsafe_fullFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_seq_cst); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"), NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"), @@ -532,6 +545,11 @@ static JNINativeMethod gMethods[] = { OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ), OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF), OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD), + + // CAS + NATIVE_METHOD(Unsafe, loadFence, "!()V"), + NATIVE_METHOD(Unsafe, storeFence, "!()V"), + NATIVE_METHOD(Unsafe, fullFence, "!()V"), }; void register_sun_misc_Unsafe(JNIEnv* env) { diff --git a/runtime/oat.cc b/runtime/oat.cc index 4948558f84..2ac105291d 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -29,6 +29,8 @@ constexpr uint8_t OatHeader::kOatMagic[4]; constexpr uint8_t OatHeader::kOatVersion[4]; constexpr const char OatHeader::kTrueValue[]; constexpr const char OatHeader::kFalseValue[]; +constexpr const char OatHeader::kExtractOnlyValue[]; +constexpr const char OatHeader::kProfileGuideCompiledValue[]; static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) { size_t estimate = 0U; @@ -467,12 +469,24 @@ bool OatHeader::IsDebuggable() const { } bool OatHeader::IsExtractOnly() const { - return IsKeyEnabled(OatHeader::kExtractOnlyKey); + return KeyHasValue(kCompilationType, + kExtractOnlyValue, + sizeof(kExtractOnlyValue)); } -bool OatHeader::IsKeyEnabled(const char* key) const { +bool OatHeader::IsProfileGuideCompiled() const { + return KeyHasValue(kCompilationType, + kProfileGuideCompiledValue, + sizeof(kProfileGuideCompiledValue)); +} + +bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const { const char* key_value = GetStoreValueByKey(key); - return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0); + return (key_value != nullptr && strncmp(key_value, value, value_size) == 0); +} + +bool OatHeader::IsKeyEnabled(const char* key) const { + return KeyHasValue(key, kTrueValue, sizeof(kTrueValue)); } void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) { diff --git a/runtime/oat.h b/runtime/oat.h index fde386f37e..0660e19ff4 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,12 +38,15 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; - static constexpr const char* kExtractOnlyKey = "extract-only"; + static constexpr const char* kCompilationType = "compilation-type"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; + static constexpr const char kExtractOnlyValue[] = "extract-only"; + static constexpr const char kProfileGuideCompiledValue[] = "profile-guide"; + static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, @@ -108,8 +111,11 @@ class PACKED(4) OatHeader { bool IsPic() const; bool IsDebuggable() const; bool IsExtractOnly() const; + bool IsProfileGuideCompiled() const; private: + bool KeyHasValue(const char* key, const char* value, size_t value_size) const; + OatHeader(InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, uint32_t dex_file_count, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c3895479b7..7155c79afb 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -169,7 +169,10 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, return false; } if (requested_base != nullptr && begin_ != requested_base) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + // Host can fail this check. Do not dump there to avoid polluting the output. + if (kIsTargetBuild) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + } *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " "oatdata=%p != expected=%p. See process maps in the log.", begin_, requested_base); @@ -1232,6 +1235,10 @@ bool OatFile::IsExtractOnly() const { return GetOatHeader().IsExtractOnly(); } +bool OatFile::IsProfileGuideCompiled() const { + return GetOatHeader().IsProfileGuideCompiled(); +} + static constexpr char kDexClassPathEncodingSeparator = '*'; std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index fb91a8cdff..1084253a88 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -93,6 +93,8 @@ class OatFile { bool IsExtractOnly() const; + bool IsProfileGuideCompiled() const; + const std::string& GetLocation() const { return location_; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 262c932766..90712c625c 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -36,7 +36,6 @@ #include "image.h" #include "oat.h" #include "os.h" -#include "profiler.h" #include "runtime.h" #include "scoped_thread_state_change.h" #include "ScopedFd.h" @@ -45,28 +44,19 @@ namespace art { OatFileAssistant::OatFileAssistant(const char* dex_location, + const int target_compilation_type_mask, const InstructionSet isa, bool load_executable) - : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { } + : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable) +{ } OatFileAssistant::OatFileAssistant(const char* dex_location, const char* oat_location, + const int target_compilation_type_mask, const InstructionSet isa, bool load_executable) - : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { } - -OatFileAssistant::OatFileAssistant(const char* dex_location, - const InstructionSet isa, - bool load_executable, - const char* package_name) - : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { } - -OatFileAssistant::OatFileAssistant(const char* dex_location, - const char* oat_location, - const InstructionSet isa, - bool load_executable, - const char* package_name) - : isa_(isa), package_name_(package_name), load_executable_(load_executable) { + : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa), + load_executable_(load_executable) { CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location"; dex_location_.assign(dex_location); @@ -83,18 +73,6 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, cached_oat_file_name_attempted_ = true; cached_oat_file_name_found_ = true; } - - // If there is no package name given, we will not be able to find any - // profiles associated with this dex location. Preemptively mark that to - // be the case, rather than trying to find and load the profiles later. - // Similarly, if profiling is disabled. - if (package_name == nullptr - || !Runtime::Current()->GetProfilerOptions().IsEnabled()) { - profile_load_attempted_ = true; - profile_load_succeeded_ = false; - old_profile_load_attempted_ = true; - old_profile_load_succeeded_ = false; - } } OatFileAssistant::~OatFileAssistant() { @@ -138,10 +116,23 @@ bool OatFileAssistant::Lock(std::string* error_msg) { return true; } -OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() { - // TODO: If the profiling code is ever restored, it's worth considering - // whether we should check to see if the profile is out of date here. +// Returns the compilation mode of the given oat file. +static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) { + if (oat_file.IsExtractOnly()) { + return OatFileAssistant::kExtractOnly; + } + if (oat_file.IsProfileGuideCompiled()) { + return OatFileAssistant::kProfileGuideCompilation; + } + // Assume that if the oat files is not extract-only or profile-guide compiled + // then it must be fully compiled. + // NB: this does not necessary mean that the oat file is actually fully compiled. It + // might have been compiled in a different way (e.g. interpret-only) which does + // not record a type in the header. + return OatFileAssistant::kFullCompilation; +} +OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() { if (OatFileIsUpToDate() || OdexFileIsUpToDate()) { return kNoDexOptNeeded; } @@ -419,6 +410,11 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& } bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) { + // Verify the file satisfies the desired compilation type. + if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) { + return true; + } + // Verify the dex checksum. // Note: GetOatDexFile will return null if the dex checksum doesn't match // what we provide, which verifies the primary dex checksum for us. @@ -541,104 +537,6 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { return true; } -bool OatFileAssistant::ProfileExists() { - return GetProfile() != nullptr; -} - -bool OatFileAssistant::OldProfileExists() { - return GetOldProfile() != nullptr; -} - -// TODO: The IsProfileChangeSignificant implementation was copied from likely -// bit-rotted code. -bool OatFileAssistant::IsProfileChangeSignificant() { - ProfileFile* profile = GetProfile(); - if (profile == nullptr) { - return false; - } - - ProfileFile* old_profile = GetOldProfile(); - if (old_profile == nullptr) { - return false; - } - - // TODO: The following code to compare two profile files should live with - // the rest of the profiler code, not the oat file assistant code. - - // A change in profile is considered significant if X% (change_thr property) - // of the top K% (compile_thr property) samples has changed. - const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions(); - const double top_k_threshold = options.GetTopKThreshold(); - const double change_threshold = options.GetTopKChangeThreshold(); - std::set<std::string> top_k, old_top_k; - profile->GetTopKSamples(top_k, top_k_threshold); - old_profile->GetTopKSamples(old_top_k, top_k_threshold); - std::set<std::string> diff; - std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(), - old_top_k.end(), std::inserter(diff, diff.end())); - - // TODO: consider using the usedPercentage instead of the plain diff count. - double change_percent = 100.0 * static_cast<double>(diff.size()) - / static_cast<double>(top_k.size()); - std::set<std::string>::iterator end = diff.end(); - for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) { - VLOG(oat) << "Profile new in topK: " << *it; - } - - if (change_percent > change_threshold) { - VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_ - << "has changed significantly: (top " - << top_k_threshold << "% samples changed in proportion of " - << change_percent << "%)"; - return true; - } - return false; -} - -// TODO: The CopyProfileFile implementation was copied from likely bit-rotted -// code. -void OatFileAssistant::CopyProfileFile() { - if (!ProfileExists()) { - return; - } - - std::string profile_name = ProfileFileName(); - std::string old_profile_name = OldProfileFileName(); - - ScopedFd src(open(old_profile_name.c_str(), O_RDONLY)); - if (src.get() == -1) { - PLOG(WARNING) << "Failed to open profile file " << old_profile_name - << ". My uid:gid is " << getuid() << ":" << getgid(); - return; - } - - struct stat stat_src; - if (fstat(src.get(), &stat_src) == -1) { - PLOG(WARNING) << "Failed to get stats for profile file " << old_profile_name - << ". My uid:gid is " << getuid() << ":" << getgid(); - return; - } - - // Create the copy with rw------- (only accessible by system) - ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600)); - if (dst.get() == -1) { - PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name - << ". My uid:gid is " << getuid() << ":" << getgid(); - return; - } - -#ifdef __linux__ - if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) { -#else - off_t len; - if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) { -#endif - PLOG(WARNING) << "Failed to copy profile file " << old_profile_name - << " to " << profile_name << ". My uid:gid is " << getuid() - << ":" << getgid(); - } -} - bool OatFileAssistant::RelocateOatFile(const std::string* input_file, std::string* error_msg) { CHECK(error_msg != nullptr); @@ -694,6 +592,15 @@ bool OatFileAssistant::RelocateOatFile(const std::string* input_file, bool OatFileAssistant::GenerateOatFile(std::string* error_msg) { CHECK(error_msg != nullptr); + // TODO: Currently we only know how to make a fully-compiled oat file. + // Perhaps we should support generating other kinds of oat files? + if ((target_compilation_type_mask_ & kFullCompilation) == 0) { + *error_msg = "Generation of oat file for dex location " + dex_location_ + + " not attempted because full compilation was not specified" + + " as an acceptable target compilation type."; + return false; + } + Runtime* runtime = Runtime::Current(); if (!runtime->IsDex2OatEnabled()) { *error_msg = "Generation of oat file for dex location " + dex_location_ @@ -861,21 +768,6 @@ std::string OatFileAssistant::DalvikCacheDirectory() { return result; } -std::string OatFileAssistant::ProfileFileName() { - if (package_name_ != nullptr) { - return DalvikCacheDirectory() + std::string("profiles/") + package_name_; - } - return ""; -} - -std::string OatFileAssistant::OldProfileFileName() { - std::string profile_name = ProfileFileName(); - if (profile_name.empty()) { - return ""; - } - return profile_name + "@old"; -} - std::string OatFileAssistant::ImageLocation() { Runtime* runtime = Runtime::Current(); const std::vector<gc::space::ImageSpace*>& image_spaces = @@ -1007,34 +899,6 @@ const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { return image_info_load_succeeded_ ? &cached_image_info_ : nullptr; } -ProfileFile* OatFileAssistant::GetProfile() { - if (!profile_load_attempted_) { - CHECK(package_name_ != nullptr) - << "pakage_name_ is nullptr: " - << "profile_load_attempted_ should have been true"; - profile_load_attempted_ = true; - std::string profile_name = ProfileFileName(); - if (!profile_name.empty()) { - profile_load_succeeded_ = cached_profile_.LoadFile(profile_name); - } - } - return profile_load_succeeded_ ? &cached_profile_ : nullptr; -} - -ProfileFile* OatFileAssistant::GetOldProfile() { - if (!old_profile_load_attempted_) { - CHECK(package_name_ != nullptr) - << "pakage_name_ is nullptr: " - << "old_profile_load_attempted_ should have been true"; - old_profile_load_attempted_ = true; - std::string old_profile_name = OldProfileFileName(); - if (!old_profile_name.empty()) { - old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name); - } - } - return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr; -} - gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) { DCHECK(oat_file != nullptr); std::string art_file = ArtFileName(oat_file); diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 7b45bca946..893aea2ab9 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -44,9 +44,6 @@ class ImageSpace; // The oat file assistant is intended to be used with dex locations not on the // boot class path. See the IsInBootClassPath method for a way to check if the // dex location is in the boot class path. -// -// TODO: All the profiling related code is old and untested. It should either -// be restored and tested, or removed. class OatFileAssistant { public: enum DexOptNeeded { @@ -73,8 +70,8 @@ class OatFileAssistant { enum OatStatus { // kOatOutOfDate - An oat file is said to be out of date if the file does - // not exist, or is out of date with respect to the dex file or boot - // image. + // not exist, is out of date with respect to the dex file or boot image, + // or does not meet the target compilation type. kOatOutOfDate, // kOatNeedsRelocation - An oat file is said to need relocation if the @@ -88,6 +85,20 @@ class OatFileAssistant { kOatUpToDate, }; + // Represents the different compilation types of oat files that OatFileAssitant + // and external GetDexOptNeeded callers care about. + // Note: these should be able to be used as part of a mask. + enum CompilationType { + // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1 + kFullCompilation = 1, + + // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2 + kProfileGuideCompilation = 2, + + // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4 + kExtractOnly = 4, + }; + // Constructs an OatFileAssistant object to assist the oat file // corresponding to the given dex location with the target instruction set. // @@ -99,31 +110,28 @@ class OatFileAssistant { // Note: Currently the dex_location must have an extension. // TODO: Relax this restriction? // + // The target compilation type specifies a set of CompilationTypes that + // should be considered up to date. An oat file compiled in a way not + // included in the set is considered out of date. For example, to consider + // otherwise up-to-date fully compiled and profile-guide compiled oat + // files as up to date, but to consider extract-only files as out of date, + // specify: (kFullCompilation | kProfileGuideCompilation). + // // The isa should be either the 32 bit or 64 bit variant for the current // device. For example, on an arm device, use arm or arm64. An oat file can // be loaded executable only if the ISA matches the current runtime. - OatFileAssistant(const char* dex_location, const InstructionSet isa, + OatFileAssistant(const char* dex_location, + int target_compilation_type_mask, + const InstructionSet isa, bool load_executable); // Constructs an OatFileAssistant, providing an explicit target oat_location // to use instead of the standard oat location. - OatFileAssistant(const char* dex_location, const char* oat_location, - const InstructionSet isa, bool load_executable); - - // Constructs an OatFileAssistant, providing an additional package_name used - // solely for the purpose of locating profile files. - // - // TODO: Why is the name of the profile file based on the package name and - // not the dex location? If there is no technical reason the dex_location - // can't be used, we should prefer that instead. - OatFileAssistant(const char* dex_location, const InstructionSet isa, - bool load_executable, const char* package_name); - - // Constructs an OatFileAssistant with user specified oat location and a - // package name. - OatFileAssistant(const char* dex_location, const char* oat_location, - const InstructionSet isa, bool load_executable, - const char* package_name); + OatFileAssistant(const char* dex_location, + const char* oat_location, + int target_compilation_type_mask, + const InstructionSet isa, + bool load_executable); ~OatFileAssistant(); @@ -233,28 +241,6 @@ class OatFileAssistant { bool GivenOatFileNeedsRelocation(const OatFile& file); bool GivenOatFileIsUpToDate(const OatFile& file); - // Returns true if there is an accessible profile associated with the dex - // location. - // This returns false if profiling is disabled. - bool ProfileExists(); - - // The old profile is a file containing a previous snapshot of profiling - // information associated with the dex file code. This is used to track how - // the profiling information has changed over time. - // - // Returns true if there is an accessible old profile associated with the - // dex location. - // This returns false if profiling is disabled. - bool OldProfileExists(); - - // Returns true if there has been a significant change between the old - // profile and the current profile. - // This returns false if profiling is disabled. - bool IsProfileChangeSignificant(); - - // Copy the current profile to the old profile location. - void CopyProfileFile(); - // Generates the oat file by relocation from the named input file. // This does not check the current status before attempting to relocate the // oat file. @@ -309,16 +295,6 @@ class OatFileAssistant { // Returns an empty string if we can't get the dalvik cache directory path. std::string DalvikCacheDirectory(); - // Constructs the filename for the profile file. - // Returns an empty string if we do not have the necessary information to - // construct the filename. - std::string ProfileFileName(); - - // Constructs the filename for the old profile file. - // Returns an empty string if we do not have the necessary information to - // construct the filename. - std::string OldProfileFileName(); - // Returns the current image location. // Returns an empty string if the image location could not be retrieved. // @@ -364,35 +340,18 @@ class OatFileAssistant { // The caller shouldn't clean up or free the returned pointer. const ImageInfo* GetImageInfo(); - // Returns the loaded profile. - // Loads the profile if needed. Returns null if the profile failed - // to load. - // The caller shouldn't clean up or free the returned pointer. - ProfileFile* GetProfile(); - - // Returns the loaded old profile. - // Loads the old profile if needed. Returns null if the old profile - // failed to load. - // The caller shouldn't clean up or free the returned pointer. - ProfileFile* GetOldProfile(); - // To implement Lock(), we lock a dummy file where the oat file would go // (adding ".flock" to the target file name) and retain the lock for the // remaining lifetime of the OatFileAssistant object. ScopedFlock flock_; std::string dex_location_; + const int target_compilation_type_mask_; // In a properly constructed OatFileAssistant object, isa_ should be either // the 32 or 64 bit variant for the current device. const InstructionSet isa_ = kNone; - // The package name, used solely to find the profile file. - // This may be null in a properly constructed object. In this case, - // profile_load_attempted_ and old_profile_load_attempted_ will be true, and - // profile_load_succeeded_ and old_profile_load_succeeded_ will be false. - const char* package_name_ = nullptr; - // Whether we will attempt to load oat files executable. bool load_executable_ = false; @@ -451,18 +410,6 @@ class OatFileAssistant { bool image_info_load_succeeded_ = false; ImageInfo cached_image_info_; - // Cached value of the profile file. - // Use the GetProfile method rather than accessing these directly. - bool profile_load_attempted_ = false; - bool profile_load_succeeded_ = false; - ProfileFile cached_profile_; - - // Cached value of the profile file. - // Use the GetOldProfile method rather than accessing these directly. - bool old_profile_load_attempted_ = false; - bool old_profile_load_succeeded_ = false; - ProfileFile cached_old_profile_; - // For debugging only. // If this flag is set, the oat or odex file has been released to the user // of the OatFileAssistant object and the OatFileAssistant object is in a diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 83d4457a1c..4541468cb3 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -260,7 +260,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { } void GenerateExtractOnlyOdexForTest(const std::string& dex_location, - const std::string& odex_location) { + const std::string& odex_location) { std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); args.push_back("--oat-file=" + odex_location); @@ -277,7 +277,26 @@ class OatFileAssistantTest : public CommonRuntimeTest { EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u); EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u); EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0); -} + } + + void GenerateProfileGuideOdexForTest(const std::string& dex_location, + const std::string& odex_location) { + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + ScratchFile profile_file; + args.push_back("--profile-file=" + profile_file.GetFilename()); + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + // Verify the odex file was generated as expected. + std::unique_ptr<OatFile> odex_file(OatFile::Open( + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, + false, dex_location.c_str(), &error_msg)); + printf("error %s", error_msg.c_str()); + ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + EXPECT_TRUE(odex_file->IsProfileGuideCompiled()); + } private: // Reserve memory around where the image will be loaded so other memory @@ -344,7 +363,8 @@ class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest { // Generate an oat file for the purposes of test, as opposed to testing // generation of oat files. static void GenerateOatForTest(const char* dex_location) { - OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location, + OatFileAssistant::kFullCompilation, kRuntimeISA, false); std::string error_msg; ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg; @@ -356,7 +376,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) { std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -379,7 +400,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) { TEST_F(OatFileAssistantTest, NoDexNoOat) { std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); @@ -400,7 +422,8 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { Copy(GetDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); @@ -422,7 +445,8 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { Copy(GetMultiDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str()); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); @@ -448,7 +472,8 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { // is out of date. Copy(GetMultiDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } @@ -475,6 +500,7 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { // Verify we can load both dex files. OatFileAssistant oat_file_assistant(dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -495,7 +521,8 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { GenerateOatForTest(dex_location.c_str()); Copy(GetDexSrc2(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); @@ -508,32 +535,6 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } -// Case: We have a DEX file and an extract-only ODEX file out of date relative -// to the DEX file. -// Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) { - std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar"; - std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex"; - - // We create a dex, generate an oat for it, then overwrite the dex with a - // different dex to make the oat out of date. - Copy(GetDexSrc1(), dex_location); - GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str()); - Copy(GetDexSrc2(), dex_location); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_TRUE(oat_file_assistant.OdexFileExists()); - EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); - EXPECT_FALSE(oat_file_assistant.OatFileExists()); - EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); - EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - // Case: We have a DEX file and an ODEX file, but no OAT file. // Expect: The status is kPatchOatNeeded. TEST_F(OatFileAssistantTest, DexOdexNoOat) { @@ -545,7 +546,8 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { GenerateOdexForTest(dex_location, odex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -578,7 +580,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -633,7 +636,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -681,7 +685,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) { Copy(GetStrippedDexSrc1(), dex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -724,7 +729,7 @@ TEST_F(OatFileAssistantTest, SelfRelocation) { GenerateOdexForTest(dex_location, oat_location); OatFileAssistant oat_file_assistant(dex_location.c_str(), - oat_location.c_str(), kRuntimeISA, true); + oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -782,7 +787,7 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // Verify things don't go bad. OatFileAssistant oat_file_assistant(dex_location.c_str(), - oat_location.c_str(), kRuntimeISA, true); + oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -816,7 +821,8 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { GeneratePicOdexForTest(dex_location, odex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -841,7 +847,9 @@ TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) { GenerateExtractOnlyOdexForTest(dex_location, odex_location); // Verify the status. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly, + kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -864,7 +872,8 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) { GenerateOatForTest(dex_location.c_str()); // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -883,7 +892,8 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) { GenerateOatForTest(dex_location.c_str()); // Load the oat using an oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -903,7 +913,8 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { Copy(GetDexSrc1(), dex_location); OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::string error_msg; ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg; @@ -917,7 +928,8 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) { EXPECT_TRUE(OS::FileExists(oat_location.c_str())); // Verify it didn't create an oat in the default location. - OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant ofm(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_FALSE(ofm.OatFileExists()); } @@ -933,7 +945,8 @@ TEST_F(OatFileAssistantTest, LoadDexUnwriteableAlternateOat) { Copy(GetDexSrc1(), dex_location); OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::string error_msg; ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg)); @@ -948,7 +961,8 @@ TEST_F(OatFileAssistantTest, GenNoDex) { std::string oat_location = GetScratchDir() + "/GenNoDex.oat"; OatFileAssistant oat_file_assistant( - dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true); + dex_location.c_str(), oat_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::string error_msg; ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg)); } @@ -996,7 +1010,8 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { Copy(GetDexSrc1(), abs_dex_location); std::string dex_location = MakePathRelative(abs_dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -1013,7 +1028,8 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) { TEST_F(OatFileAssistantTest, ShortDexLocation) { std::string dex_location = "/xx"; - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -1037,7 +1053,8 @@ TEST_F(OatFileAssistantTest, LongDexExtension) { std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx"; Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); @@ -1134,7 +1151,8 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) { GenerateOdexForTest(dex_location, odex_location); // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1156,7 +1174,8 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) { GenerateOdexForTest(dex_location, odex_location); // Load the oat using an executable oat file assistant. - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true); + OatFileAssistant oat_file_assistant(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, true); std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile(); ASSERT_TRUE(oat_file.get() != nullptr); @@ -1184,6 +1203,45 @@ TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) { "/foo/bar/baz_noext", kArm, &odex_file, &error_msg)); } +// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT. +// Expect: The status depends on the target compilation type mask. +TEST_F(OatFileAssistantTest, TargetCompilationType) { + std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar"; + std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex"; + Copy(GetDexSrc1(), dex_location); + GenerateExtractOnlyOdexForTest(dex_location, odex_location); + GenerateOatForTest(dex_location.c_str()); + + OatFileAssistant ofa_full(dex_location.c_str(), + OatFileAssistant::kFullCompilation, kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded()); + EXPECT_FALSE(ofa_full.IsInBootClassPath()); + EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate()); + EXPECT_TRUE(ofa_full.OatFileIsUpToDate()); + + OatFileAssistant ofa_extract(dex_location.c_str(), + OatFileAssistant::kExtractOnly, kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded()); + EXPECT_FALSE(ofa_extract.IsInBootClassPath()); + EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate()); + EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate()); + + OatFileAssistant ofa_profile(dex_location.c_str(), + OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded()); + EXPECT_FALSE(ofa_profile.IsInBootClassPath()); + EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate()); + EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate()); + + OatFileAssistant ofa_extract_full(dex_location.c_str(), + OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly, + kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded()); + EXPECT_FALSE(ofa_extract_full.IsInBootClassPath()); + EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate()); + EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate()); +} + // Verify the dexopt status values from dalvik.system.DexFile // match the OatFileAssistant::DexOptStatus values. TEST_F(OatFileAssistantTest, DexOptStatusValues) { @@ -1218,13 +1276,31 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { ASSERT_FALSE(self_patchoat_needed == nullptr); EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt); EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get())); + + ArtField* compilation_type_full = mirror::Class::FindStaticField( + soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I"); + ASSERT_FALSE(compilation_type_full == nullptr); + EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get())); + + ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField( + soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I"); + ASSERT_FALSE(compilation_type_profile_guide == nullptr); + EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation, + compilation_type_profile_guide->GetInt(dexfile.Get())); + + ArtField* compilation_type_extract_only = mirror::Class::FindStaticField( + soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I"); + ASSERT_FALSE(compilation_type_extract_only == nullptr); + EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt); + EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get())); } // TODO: More Tests: // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat // * Test using secondary isa -// * Test with profiling info? // * Test for status of oat while oat is being generated (how?) // * Test case where 32 and 64 bit boot class paths differ, // and we ask IsInBootClassPath for a class in exactly one of the 32 or @@ -1233,5 +1309,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { // - Dex is stripped, don't have odex. // - Oat file corrupted after status check, before reload unexecutable // because it's unrelocated and no dex2oat +// * Test unrelocated specific target compilation type can be relocated to +// make it up to date. } // namespace art diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 9ae179fc5b..e57125bef1 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -307,8 +307,13 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( Thread* const self = Thread::Current(); Locks::mutator_lock_->AssertNotHeld(self); Runtime* const runtime = Runtime::Current(); + + int target_compilation_type_mask = OatFileAssistant::kFullCompilation + | OatFileAssistant::kProfileGuideCompilation + | OatFileAssistant::kExtractOnly; OatFileAssistant oat_file_assistant(dex_location, oat_location, + target_compilation_type_mask, kRuntimeISA, !runtime->IsAotCompiler()); @@ -443,6 +448,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( + std::string(dex_location)); } } + + // TODO(calin): Consider optimizing this knowing that is useless to record the + // use of fully compiled apks. + Runtime::Current()->NotifyDexLoaded(dex_location); return dex_files; } diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc index 9b10f2e0b8..c7ccee2125 100644 --- a/runtime/quick/inline_method_analyser.cc +++ b/runtime/quick/inline_method_analyser.cc @@ -744,9 +744,12 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, return false; } DCHECK_GE(field->GetOffset().Int32Value(), 0); + // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. + uint32_t field_offset = field->GetOffset().Uint32Value(); + bool is_volatile = field->IsVolatile(); result->field_idx = field_idx; - result->field_offset = field->GetOffset().Int32Value(); - result->is_volatile = field->IsVolatile(); + result->field_offset = field_offset; + result->is_volatile = is_volatile ? 1u : 0u; return true; } diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 7e84b405e7..0e12d73595 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -101,6 +101,17 @@ enum InlineMethodOpcode : uint16_t { kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, + + // 1.8. + kIntrinsicUnsafeGetAndAddInt, + kIntrinsicUnsafeGetAndAddLong, + kIntrinsicUnsafeGetAndSetInt, + kIntrinsicUnsafeGetAndSetLong, + kIntrinsicUnsafeGetAndSetObject, + kIntrinsicUnsafeLoadFence, + kIntrinsicUnsafeStoreFence, + kIntrinsicUnsafeFullFence, + kIntrinsicSystemArrayCopyCharArray, kIntrinsicSystemArrayCopy, diff --git a/runtime/runtime.cc b/runtime/runtime.cc index bbb79af1bb..e95f2c539f 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -119,6 +119,7 @@ #include "os.h" #include "parsed_options.h" #include "profiler.h" +#include "jit/profile_saver.h" #include "quick/quick_method_frame_info.h" #include "reflection.h" #include "runtime_options.h" @@ -1700,7 +1701,9 @@ void Runtime::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) { } void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, - const std::string& profile_output_filename) { + const std::string& profile_output_filename, + const std::string& foreign_dex_profile_path, + const std::string& app_dir) { if (jit_.get() == nullptr) { // We are not JITing. Nothing to do. return; @@ -1723,7 +1726,18 @@ void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, } profile_output_filename_ = profile_output_filename; - jit_->StartProfileSaver(profile_output_filename, code_paths); + jit_->StartProfileSaver(profile_output_filename, + code_paths, + foreign_dex_profile_path, + app_dir); +} + +void Runtime::NotifyDexLoaded(const std::string& dex_location) { + VLOG(profiler) << "Notify dex loaded: " << dex_location; + // We know that if the ProfileSaver is started then we can record profile information. + if (ProfileSaver::IsStarted()) { + ProfileSaver::NotifyDexUse(dex_location); + } } // Transaction support. diff --git a/runtime/runtime.h b/runtime/runtime.h index 83e77d2372..8e99f800e0 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -467,7 +467,10 @@ class Runtime { } void RegisterAppInfo(const std::vector<std::string>& code_paths, - const std::string& profile_output_filename); + const std::string& profile_output_filename, + const std::string& foreign_dex_profile_path, + const std::string& app_dir); + void NotifyDexLoaded(const std::string& dex_location); // Transaction support. bool IsActiveTransaction() const { diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index 8237b06a56..bc963c5b8c 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -36,6 +36,7 @@ namespace art { static constexpr bool kDumpHeapObjectOnSigsevg = false; static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; struct Backtrace { public: @@ -350,7 +351,9 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex if (runtime != nullptr) { if (IsTimeoutSignal(signal_number)) { // Special timeout signal. Try to dump all threads. - runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL)); + // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts + // are of value here. + runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout); } gc::Heap* heap = runtime->GetHeap(); LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage(); diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index cf515b60cb..4c81d4f1e3 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -171,9 +171,9 @@ void ThreadList::DumpUnattachedThreads(std::ostream& os) { closedir(d); } -// Dump checkpoint timeout in milliseconds. Larger amount on the host, as dumping will invoke -// addr2line when available. -static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000; +// Dump checkpoint timeout in milliseconds. Larger amount on the target, since the device could be +// overloaded with ANR dumps. +static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 100000 : 20000; // A closure used by Thread::Dump. class DumpCheckpoint FINAL : public Closure { diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 30f613c389..b171b75e97 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -17,6 +17,7 @@ #include "reg_type_cache-inl.h" #include "base/arena_bit_vector.h" +#include "base/bit_vector-inl.h" #include "base/casts.h" #include "base/scoped_arena_allocator.h" #include "base/stl_util.h" @@ -351,9 +352,11 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegT types.Copy(&left_merge->GetUnresolvedTypes()); left_resolved = &left_merge->GetResolvedPart(); } else if (left.IsUnresolvedTypes()) { + types.ClearAllBits(); types.SetBit(left.GetId()); left_resolved = &Zero(); } else { + types.ClearAllBits(); left_resolved = &left; } diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 22ac7e4ab2..42a74f88e1 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -30,23 +30,14 @@ namespace art { namespace verifier { -class BaseRegTypeTest : public CommonRuntimeTest { - public: - void PostRuntimeCreate() OVERRIDE { - stack.reset(new ArenaStack(Runtime::Current()->GetArenaPool())); - allocator.reset(new ScopedArenaAllocator(stack.get())); - } - - std::unique_ptr<ArenaStack> stack; - std::unique_ptr<ScopedArenaAllocator> allocator; -}; - -class RegTypeTest : public BaseRegTypeTest {}; +class RegTypeTest : public CommonRuntimeTest {}; TEST_F(RegTypeTest, ConstLoHi) { // Tests creating primitive types types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_const_0 = cache.FromCat1Const(10, true); const RegType& ref_type_const_1 = cache.FromCat1Const(10, true); const RegType& ref_type_const_2 = cache.FromCat1Const(30, true); @@ -67,8 +58,10 @@ TEST_F(RegTypeTest, ConstLoHi) { } TEST_F(RegTypeTest, Pairs) { + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); int64_t val = static_cast<int32_t>(1234); const RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true); const RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true); @@ -91,8 +84,10 @@ TEST_F(RegTypeTest, Pairs) { } TEST_F(RegTypeTest, Primitives) { + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& bool_reg_type = cache.Boolean(); EXPECT_FALSE(bool_reg_type.IsUndefined()); @@ -359,13 +354,15 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(double_reg_type.HasClass()); } -class RegTypeReferenceTest : public BaseRegTypeTest {}; +class RegTypeReferenceTest : public CommonRuntimeTest {}; TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { // Tests matching precisions. A reference type that was created precise doesn't // match the one that is imprecise. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& imprecise_obj = cache.JavaLangObject(false); const RegType& precise_obj = cache.JavaLangObject(true); const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); @@ -379,8 +376,10 @@ TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { TEST_F(RegTypeReferenceTest, UnresolvedType) { // Tests creating unresolved types. Miss for the first time asking the cache and // a hit second time. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); @@ -395,8 +394,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedType) { TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { // Tests creating types uninitialized types from unresolved types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); @@ -417,8 +418,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { TEST_F(RegTypeReferenceTest, Dump) { // Tests types for proper Dump messages. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true); const RegType& resolved_ref = cache.JavaLangString(); @@ -442,8 +445,10 @@ TEST_F(RegTypeReferenceTest, JavalangString) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then check for the same effect when using // The JavaLangObject method instead of FromDescriptor. String class is final. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type = cache.JavaLangString(); const RegType& ref_type_2 = cache.JavaLangString(); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true); @@ -462,8 +467,10 @@ TEST_F(RegTypeReferenceTest, JavalangObject) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then I am checking for the same effect when using // The JavaLangObject method instead of FromDescriptor. Object Class in not final. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type = cache.JavaLangObject(true); const RegType& ref_type_2 = cache.JavaLangObject(true); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); @@ -476,7 +483,9 @@ TEST_F(RegTypeReferenceTest, Merging) { // Tests merging logic // String and object , LUB is object. ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + RegTypeCache cache_new(true, allocator); const RegType& string = cache_new.JavaLangString(); const RegType& Object = cache_new.JavaLangObject(true); EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject()); @@ -498,8 +507,10 @@ TEST_F(RegTypeReferenceTest, Merging) { TEST_F(RegTypeTest, MergingFloat) { // Testing merging logic with float and float constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& float_type = cache_new.Float(); @@ -529,8 +540,10 @@ TEST_F(RegTypeTest, MergingFloat) { TEST_F(RegTypeTest, MergingLong) { // Testing merging logic with long and long constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& long_lo_type = cache_new.LongLo(); @@ -583,8 +596,10 @@ TEST_F(RegTypeTest, MergingLong) { TEST_F(RegTypeTest, MergingDouble) { // Testing merging logic with double and double constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& double_lo_type = cache_new.DoubleLo(); @@ -637,8 +652,10 @@ TEST_F(RegTypeTest, MergingDouble) { TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); const RegType& imprecise_const = cache_new.FromCat1Const(10, false); const RegType& precise_const = cache_new.FromCat1Const(10, true); diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index a9a7a058e0..b2f905e0ee 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -40,7 +40,7 @@ public class Main { } private static Unsafe getUnsafe() throws Exception { - Class<?> unsafeClass = Class.forName("sun.misc.Unsafe"); + Class<?> unsafeClass = Unsafe.class; Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); diff --git a/test/004-checker-UnsafeTest18/expected.txt b/test/004-checker-UnsafeTest18/expected.txt new file mode 100644 index 0000000000..651da727af --- /dev/null +++ b/test/004-checker-UnsafeTest18/expected.txt @@ -0,0 +1,2 @@ +starting +passed diff --git a/test/004-checker-UnsafeTest18/info.txt b/test/004-checker-UnsafeTest18/info.txt new file mode 100644 index 0000000000..0fca5ebf03 --- /dev/null +++ b/test/004-checker-UnsafeTest18/info.txt @@ -0,0 +1 @@ +Test support for 1.8 sun.misc.Unsafe. diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java new file mode 100644 index 0000000000..bb020b9b9f --- /dev/null +++ b/test/004-checker-UnsafeTest18/src/Main.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +/** + * Checker test on the 1.8 unsafe operations. Note, this is by no means an + * exhaustive unit test for these CAS (compare-and-swap) and fence operations. + * Instead, this test ensures the methods are recognized as intrinsic and behave + * as expected. + */ +public class Main { + + private static final Unsafe unsafe = getUnsafe(); + + private static Thread[] sThreads = new Thread[10]; + + // + // Fields accessed by setters and adders. + // + + public int i = 0; + public long l = 0; + public Object o = null; + + // + // Setters. + // + + /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after) + /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetInt + /// CHECK-DAG: Return [<<Result>>] + private static int set32(Object o, long offset, int newValue) { + return unsafe.getAndSetInt(o, offset, newValue); + } + + /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after) + /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetLong + /// CHECK-DAG: Return [<<Result>>] + private static long set64(Object o, long offset, long newValue) { + return unsafe.getAndSetLong(o, offset, newValue); + } + + /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after) + /// CHECK-DAG: <<Result:l\d+>> InvokeVirtual intrinsic:UnsafeGetAndSetObject + /// CHECK-DAG: Return [<<Result>>] + private static Object setObj(Object o, long offset, Object newValue) { + return unsafe.getAndSetObject(o, offset, newValue); + } + + // + // Adders. + // + + /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after) + /// CHECK-DAG: <<Result:i\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddInt + /// CHECK-DAG: Return [<<Result>>] + private static int add32(Object o, long offset, int delta) { + return unsafe.getAndAddInt(o, offset, delta); + } + + /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after) + /// CHECK-DAG: <<Result:j\d+>> InvokeVirtual intrinsic:UnsafeGetAndAddLong + /// CHECK-DAG: Return [<<Result>>] + private static long add64(Object o, long offset, long delta) { + return unsafe.getAndAddLong(o, offset, delta); + } + + // + // Fences (native). + // + + /// CHECK-START: void Main.load() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence + // + /// CHECK-START: void Main.load() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence + // + /// CHECK-START: void Main.load() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:LoadAny + private static void load() { + unsafe.loadFence(); + } + + /// CHECK-START: void Main.store() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence + // + /// CHECK-START: void Main.store() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence + // + /// CHECK-START: void Main.store() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:AnyStore + private static void store() { + unsafe.storeFence(); + } + + /// CHECK-START: void Main.full() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence + // + /// CHECK-START: void Main.full() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence + // + /// CHECK-START: void Main.full() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:AnyAny + private static void full() { + unsafe.fullFence(); + } + + // + // Thread fork/join. + // + + private static void fork(Runnable r) { + for (int i = 0; i < 10; i++) { + sThreads[i] = new Thread(r); + sThreads[i].start(); + } + } + + private static void join() { + try { + for (int i = 0; i < 10; i++) { + sThreads[i].join(); + } + } catch (InterruptedException e) { + throw new Error("Failed join: " + e); + } + } + + // + // Driver. + // + + public static void main(String[] args) { + System.out.println("starting"); + + final Main m = new Main(); + + // Get the offsets. + + final long intOffset, longOffset, objOffset; + try { + Field intField = Main.class.getDeclaredField("i"); + Field longField = Main.class.getDeclaredField("l"); + Field objField = Main.class.getDeclaredField("o"); + + intOffset = unsafe.objectFieldOffset(intField); + longOffset = unsafe.objectFieldOffset(longField); + objOffset = unsafe.objectFieldOffset(objField); + + } catch (NoSuchFieldException e) { + throw new Error("No offset: " + e); + } + + // Some sanity within same thread. + + set32(m, intOffset, 3); + expectEquals32(3, m.i); + + set64(m, longOffset, 7L); + expectEquals64(7L, m.l); + + setObj(m, objOffset, m); + expectEqualsObj(m, m.o); + + add32(m, intOffset, 11); + expectEquals32(14, m.i); + + add64(m, longOffset, 13L); + expectEquals64(20L, m.l); + + // Some sanity on setters within different threads. + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + set32(m, intOffset, i); + } + }); + join(); + expectEquals32(9, m.i); // one thread's last value wins + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + set64(m, longOffset, (long) (100 + i)); + } + }); + join(); + expectEquals64(109L, m.l); // one thread's last value wins + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + setObj(m, objOffset, sThreads[i]); + } + }); + join(); + expectEqualsObj(sThreads[9], m.o); // one thread's last value wins + + // Some sanity on adders within different threads. + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + add32(m, intOffset, i + 1); + } + }); + join(); + expectEquals32(559, m.i); // all values accounted for + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + add64(m, longOffset, (long) (i + 1)); + } + }); + join(); + expectEquals64(659L, m.l); // all values accounted for + + // TODO: the fences + + System.out.println("passed"); + } + + // Use reflection to implement "Unsafe.getUnsafe()"; + private static Unsafe getUnsafe() { + try { + Class<?> unsafeClass = Unsafe.class; + Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } catch (Exception e) { + throw new Error("Cannot get Unsafe instance"); + } + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEqualsObj(Object expected, Object result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java index 752fdd9135..418690a2a6 100644 --- a/test/145-alloc-tracking-stress/src/Main.java +++ b/test/145-alloc-tracking-stress/src/Main.java @@ -1,5 +1,4 @@ /* - * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/462-checker-inlining-across-dex-files/multidex.jpp b/test/462-checker-inlining-across-dex-files/multidex.jpp new file mode 100644 index 0000000000..ae554566cb --- /dev/null +++ b/test/462-checker-inlining-across-dex-files/multidex.jpp @@ -0,0 +1,8 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main + +AAA: + @@com.android.jack.annotations.ForceInMainDex + class AAA + diff --git a/test/556-invoke-super/multidex.jpp b/test/556-invoke-super/multidex.jpp new file mode 100644 index 0000000000..fe018019e3 --- /dev/null +++ b/test/556-invoke-super/multidex.jpp @@ -0,0 +1,4 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main* + diff --git a/test/569-checker-pattern-replacement/multidex.jpp b/test/569-checker-pattern-replacement/multidex.jpp new file mode 100644 index 0000000000..cfc8ad1fc9 --- /dev/null +++ b/test/569-checker-pattern-replacement/multidex.jpp @@ -0,0 +1,8 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main + +BaseInMainDex: + @@com.android.jack.annotations.ForceInMainDex + class BaseInMainDex + diff --git a/test/577-profile-foreign-dex/expected.txt b/test/577-profile-foreign-dex/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/577-profile-foreign-dex/expected.txt diff --git a/test/577-profile-foreign-dex/info.txt b/test/577-profile-foreign-dex/info.txt new file mode 100644 index 0000000000..090db3fdc6 --- /dev/null +++ b/test/577-profile-foreign-dex/info.txt @@ -0,0 +1 @@ +Check that we record the use of foreign dex files when profiles are enabled. diff --git a/test/577-profile-foreign-dex/run b/test/577-profile-foreign-dex/run new file mode 100644 index 0000000000..ad57d14c60 --- /dev/null +++ b/test/577-profile-foreign-dex/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +exec ${RUN} \ + --runtime-option -Xjitsaveprofilinginfo \ + --runtime-option -Xusejit:true \ + "${@}" diff --git a/test/577-profile-foreign-dex/src-ex/OtherDex.java b/test/577-profile-foreign-dex/src-ex/OtherDex.java new file mode 100644 index 0000000000..cba73b3094 --- /dev/null +++ b/test/577-profile-foreign-dex/src-ex/OtherDex.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class OtherDex { +} diff --git a/test/577-profile-foreign-dex/src/Main.java b/test/577-profile-foreign-dex/src/Main.java new file mode 100644 index 0000000000..0cd85b58e8 --- /dev/null +++ b/test/577-profile-foreign-dex/src/Main.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.util.HashMap; + +public class Main { + + private static final String PROFILE_NAME = "primary.prof"; + private static final String APP_DIR_PREFIX = "app_dir_"; + private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex"; + private static final String TEMP_FILE_NAME_PREFIX = "dummy"; + private static final String TEMP_FILE_NAME_SUFFIX = "-file"; + + public static void main(String[] args) throws Exception { + File tmpFile = null; + File appDir = null; + File profileFile = null; + File foreignDexProfileDir = null; + + try { + // Create the necessary files layout. + tmpFile = createTempFile(); + appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName()); + appDir.mkdir(); + foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR); + foreignDexProfileDir.mkdir(); + profileFile = createTempFile(); + + String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; + + // Register the app with the runtime + VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(), + new String[] { codePath }, foreignDexProfileDir.getPath()); + + testMarkerForForeignDex(foreignDexProfileDir); + testMarkerForCodePath(foreignDexProfileDir); + testMarkerForApplicationDexFile(foreignDexProfileDir, appDir); + } finally { + if (tmpFile != null) { + tmpFile.delete(); + } + if (profileFile != null) { + profileFile.delete(); + } + if (foreignDexProfileDir != null) { + foreignDexProfileDir.delete(); + } + if (appDir != null) { + appDir.delete(); + } + } + } + + // Verify we actually create a marker on disk for foreign dex files. + private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception { + String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"; + loadDexFile(foreignDex); + checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true); + } + + // Verify we do not create a marker on disk for dex files path of the code path. + private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception { + String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; + loadDexFile(codePath); + checkMarker(foreignDexProfileDir, codePath, /* exists */ false); + } + + private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir) + throws Exception { + // Copy the -ex jar to the application directory and load it from there. + // This will record duplicate class conflicts but we don't care for this use case. + File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"); + File appDex = new File(appDir, "appDex.jar"); + try { + copyFile(foreignDex, appDex); + + loadDexFile(appDex.getAbsolutePath()); + checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false); + } finally { + if (appDex != null) { + appDex.delete(); + } + } + } + + private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) { + File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@')); + boolean result_ok = exists ? marker.exists() : !marker.exists(); + if (!result_ok) { + throw new RuntimeException("Marker test failed for:" + marker.getPath()); + } + } + + private static void loadDexFile(String dexFile) throws Exception { + Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + if (pathClassLoader == null) { + throw new RuntimeException("Couldn't find path class loader class"); + } + Constructor constructor = + pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); + constructor.newInstance( + dexFile, ClassLoader.getSystemClassLoader()); + } + + private static class VMRuntime { + private static final Method registerAppInfoMethod; + static { + try { + Class c = Class.forName("dalvik.system.VMRuntime"); + registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo", + String.class, String.class, String[].class, String.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void registerAppInfo(String pkgName, String appDir, + String[] codePath, String foreignDexProfileDir) throws Exception { + registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir); + } + } + + private static void copyFile(File fromFile, File toFile) throws Exception { + FileInputStream in = new FileInputStream(fromFile); + FileOutputStream out = new FileOutputStream(toFile); + try { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) >= 0) { + out.write(buffer, 0, bytesRead); + } + } finally { + out.flush(); + try { + out.getFD().sync(); + } catch (IOException e) { + } + out.close(); + in.close(); + } + } + + private static File createTempFile() throws Exception { + try { + return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); + } catch (IOException e) { + System.setProperty("java.io.tmpdir", "/data/local/tmp"); + try { + return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); + } catch (IOException e2) { + System.setProperty("java.io.tmpdir", "/sdcard"); + return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); + } + } + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index c4f0171f0d..7036bdcaf5 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -567,7 +567,9 @@ TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ 537-checker-arraycopy # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler). -TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := +# 145: Test sometimes times out in read barrier configuration (b/27467554). +TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress ifeq ($(ART_USE_READ_BARRIER),true) ifneq (,$(filter interpreter,$(COMPILER_TYPES))) diff --git a/test/etc/default-build b/test/etc/default-build index 6e855ec30a..5f78496c3f 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -116,28 +116,33 @@ if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ]; then SKIP_DX_MERGER="true" fi -if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then - # Jack does not support this configuration unless we specify how to partition the DEX file - # with a .jpp file. - USE_JACK="false" -fi - if [ ${USE_JACK} = "true" ]; then # Jack toolchain if [ "${HAS_SRC}" = "true" ]; then - ${JACK} ${JACK_ARGS} --output-jack src.jack src - imported_jack_files="--import src.jack" + if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then + # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning + # when creating the output .dex file. + ${JACK} ${JACK_ARGS} --output-jack src.jack src src src-multidex + jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex" + jack_extra_args="${jack_extra_args} -D jack.preprocessor=true" + jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp" + else + ${JACK} ${JACK_ARGS} --output-jack src.jack src + fi + jack_extra_args="${jack_extra_args} --import src.jack" fi if [ "${HAS_SRC2}" = "true" ]; then ${JACK} ${JACK_ARGS} --output-jack src2.jack src2 - imported_jack_files="--import src2.jack ${imported_jack_files}" + # In case of duplicate classes, we want to take into account the classes from src2. Therefore + # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file. + jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first" + jack_extra_args="--import src2.jack ${jack_extra_args}" fi - # Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider - # class definitions from src2 first. + # Compile jack files into a DEX file. if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then - ${JACK} ${JACK_ARGS} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex . + ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex . fi else # Legacy toolchain with javac+dx diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index fab4599925..46100ae15c 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -270,10 +270,5 @@ description: "Only work with --mode=activity", result: EXEC_FAILED, names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ] -}, -{ - description: "Temporary suppressing while test is fixed", - result: EXEC_FAILED, - names: [ "org.apache.harmony.tests.java.util.ArrayDequeTest#test_forEachRemaining_iterator" ] } ] diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index 19a61dc8cb..95f0c2dcf2 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -16,20 +16,5 @@ names: ["jsr166.LinkedTransferQueueTest#testTransfer2", "jsr166.LinkedTransferQueueTest#testWaitingConsumer"], bug: 25883050 -}, -{ - description: "libcore.java.lang.OldSystemTest#test_gc failure on armv8-concurrent-collector.", - result: EXEC_FAILED, - names: ["libcore.java.lang.OldSystemTest#test_gc"], - bug: 26155567 -}, -{ - description: "TimeoutException on hammerhead-concurrent-collector", - result: EXEC_FAILED, - modes: [device], - names: ["libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", - "libcore.java.text.SimpleDateFormatTest#testLocales", - "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries"], - bug: 26711853 } ] diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index 9e085b58b4..1e9c763534 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -46,5 +46,13 @@ adb logcat -P "" adb logcat -p echo -e "${green}Kill stalled dalvikvm processes${nc}" -processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') -for i in $processes; do adb shell kill -9 $i; done +# 'ps' on M can sometimes hang. +timeout 2s adb shell "ps" +if [ $? = 124 ]; then + echo -e "${green}Rebooting device to fix 'ps'${nc}" + adb reboot + adb wait-for-device root +else + processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') + for i in $processes; do adb shell kill -9 $i; done +fi |