summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/art_method-inl.h6
-rw-r--r--runtime/art_method.cc2
-rw-r--r--runtime/base/unix_file/fd_file.cc19
-rw-r--r--runtime/base/unix_file/fd_file.h4
-rw-r--r--runtime/base/unix_file/fd_file_test.cc28
-rw-r--r--runtime/class_linker.cc388
-rw-r--r--runtime/common_runtime_test.cc1
-rw-r--r--runtime/gc/collector/mark_compact.cc2
-rw-r--r--runtime/gc/heap.cc1
-rw-r--r--runtime/gc/heap.h4
-rw-r--r--runtime/gc/space/image_space.cc63
-rw-r--r--runtime/gc/space/large_object_space.cc2
-rw-r--r--runtime/interpreter/mterp/mips/binop.S4
-rw-r--r--runtime/interpreter/mterp/mips64/bincmp.S30
-rw-r--r--runtime/interpreter/mterp/mips64/footer.S20
-rw-r--r--runtime/interpreter/mterp/mips64/header.S9
-rw-r--r--runtime/interpreter/mterp/mips64/invoke.S3
-rw-r--r--runtime/interpreter/mterp/mips64/op_goto.S28
-rw-r--r--runtime/interpreter/mterp/mips64/op_goto_16.S26
-rw-r--r--runtime/interpreter/mterp/mips64/op_goto_32.S28
-rw-r--r--runtime/interpreter/mterp/mips64/op_packed_switch.S23
-rw-r--r--runtime/interpreter/mterp/mips64/zcmp.S30
-rw-r--r--runtime/interpreter/mterp/mterp.cc11
-rw-r--r--runtime/interpreter/mterp/out/mterp_mips64.S553
-rw-r--r--runtime/jit/jit.cc6
-rw-r--r--runtime/jit/jit.h12
-rw-r--r--runtime/jit/jit_code_cache.cc8
-rw-r--r--runtime/jit/profile_saver.cc126
-rw-r--r--runtime/jit/profile_saver.h18
-rw-r--r--runtime/mem_map.cc14
-rw-r--r--runtime/mirror/class-inl.h7
-rw-r--r--runtime/native/dalvik_system_DexFile.cc54
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc24
-rw-r--r--runtime/native/sun_misc_Unsafe.cc18
-rw-r--r--runtime/oat.cc20
-rw-r--r--runtime/oat.h8
-rw-r--r--runtime/oat_file.cc9
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/oat_file_assistant.cc208
-rw-r--r--runtime/oat_file_assistant.h117
-rw-r--r--runtime/oat_file_assistant_test.cc188
-rw-r--r--runtime/oat_file_manager.cc9
-rw-r--r--runtime/quick/inline_method_analyser.cc7
-rw-r--r--runtime/quick/inline_method_analyser.h11
-rw-r--r--runtime/runtime.cc18
-rw-r--r--runtime/runtime.h5
-rw-r--r--runtime/runtime_linux.cc5
-rw-r--r--runtime/thread_list.cc6
-rw-r--r--runtime/verifier/reg_type_cache.cc3
-rw-r--r--runtime/verifier/reg_type_test.cc71
50 files changed, 1264 insertions, 995 deletions
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);