Merge "Patch for compiling art to run on linux target"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index e9b6a6d..3b459c3 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -172,6 +172,7 @@
runtime/arch/x86/instruction_set_features_x86_test.cc \
runtime/arch/x86_64/instruction_set_features_x86_64_test.cc \
runtime/barrier_test.cc \
+ runtime/base/arena_allocator_test.cc \
runtime/base/bit_field_test.cc \
runtime/base/bit_utils_test.cc \
runtime/base/bit_vector_test.cc \
@@ -274,7 +275,6 @@
compiler/optimizing/ssa_test.cc \
compiler/optimizing/stack_map_test.cc \
compiler/optimizing/suspend_check_test.cc \
- compiler/utils/arena_allocator_test.cc \
compiler/utils/dedupe_set_test.cc \
compiler/utils/intrusive_forward_list_test.cc \
compiler/utils/swap_space_test.cc \
@@ -650,11 +650,11 @@
ifeq ($(ART_BUILD_TARGET),true)
$(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,libbacktrace)))
- $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler libbacktrace)))
+ $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler libbacktrace libnativeloader)))
endif
ifeq ($(ART_BUILD_HOST),true)
$(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,libbacktrace)))
- $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler libbacktrace)))
+ $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler libbacktrace libnativeloader)))
endif
# Used outside the art project to get a list of the current tests
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 81b854e..7c53e01 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -461,8 +461,8 @@
* Test successes
*/
{
- EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJIT);
- EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJIT);
+ EXPECT_SINGLE_PARSE_VALUE(true, "-Xusejit:true", M::UseJitCompilation);
+ EXPECT_SINGLE_PARSE_VALUE(false, "-Xusejit:false", M::UseJitCompilation);
}
{
EXPECT_SINGLE_PARSE_VALUE(
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index c0a00cc..4797540 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -620,6 +620,8 @@
log_verbosity.verifier = true;
} else if (verbose_options[j] == "image") {
log_verbosity.image = true;
+ } else if (verbose_options[j] == "systrace-locks") {
+ log_verbosity.systrace_lock_logging = true;
} else {
return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
}
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index f75a252..bf29e1c 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -180,6 +180,7 @@
isa,
instruction_set_features_.get(),
/* boot_image */ true,
+ /* app_image */ false,
GetImageClasses(),
GetCompiledClasses(),
GetCompiledMethods(),
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 1491a18..606302b 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -60,7 +60,7 @@
// TODO: Investigate why are we doing the work again for this method and try to avoid it.
LOG(WARNING) << "Method processed more than once: "
<< PrettyMethod(ref.dex_method_index, *ref.dex_file);
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
DCHECK_EQ(it->second->GetDevirtMap().size(), verified_method->GetDevirtMap().size());
DCHECK_EQ(it->second->GetSafeCastSet().size(), verified_method->GetSafeCastSet().size());
}
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 5c0253c..bace014 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -54,7 +54,8 @@
}
// Only need dequicken info for JIT so far.
- if (Runtime::Current()->UseJit() && !verified_method->GenerateDequickenMap(method_verifier)) {
+ if (Runtime::Current()->UseJitCompilation() &&
+ !verified_method->GenerateDequickenMap(method_verifier)) {
return nullptr;
}
}
@@ -72,7 +73,7 @@
}
const DexFileReference* VerifiedMethod::GetDequickenIndex(uint32_t dex_pc) const {
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
auto it = dequicken_map_.find(dex_pc);
return (it != dequicken_map_.end()) ? &it->second : nullptr;
}
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 9e0c22c..6863f42 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -36,6 +36,7 @@
/* instruction_set_ */ kNone,
/* instruction_set_features */ nullptr,
/* boot_image */ false,
+ /* app_image */ false,
/* image_classes */ nullptr,
/* compiled_classes */ nullptr,
/* compiled_methods */ nullptr,
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index be82956..1ab1d31 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -341,6 +341,7 @@
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
bool boot_image,
+ bool app_image,
std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
@@ -363,6 +364,7 @@
compiled_methods_(MethodTable::key_compare()),
non_relative_linker_patch_count_(0u),
boot_image_(boot_image),
+ app_image_(app_image),
image_classes_(image_classes),
classes_to_compile_(compiled_classes),
methods_to_compile_(compiled_methods),
@@ -473,7 +475,7 @@
const DexFile& dex_file, const DexFile::ClassDef& class_def)
SHARED_REQUIRES(Locks::mutator_lock_) {
auto* const runtime = Runtime::Current();
- if (runtime->UseJit() || driver.GetCompilerOptions().VerifyAtRuntime()) {
+ if (runtime->UseJitCompilation() || driver.GetCompilerOptions().VerifyAtRuntime()) {
// Verify at runtime shouldn't dex to dex since we didn't resolve of verify.
return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
}
@@ -945,7 +947,7 @@
class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
public:
- ResolveCatchBlockExceptionsClassVisitor(
+ explicit ResolveCatchBlockExceptionsClassVisitor(
std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
: exceptions_to_resolve_(exceptions_to_resolve) {}
@@ -1268,7 +1270,7 @@
bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) {
Runtime* runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) {
- DCHECK(runtime->UseJit());
+ DCHECK(runtime->UseJitCompilation());
// Having the klass reference here implies that the klass is already loaded.
return true;
}
@@ -1289,7 +1291,7 @@
if ((IsBootImage() &&
IsImageClass(dex_cache->GetDexFile()->StringDataByIdx(
dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) ||
- Runtime::Current()->UseJit()) {
+ Runtime::Current()->UseJitCompilation()) {
mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
result = (resolved_class != nullptr);
}
@@ -1307,7 +1309,7 @@
// See also Compiler::ResolveDexFile
bool result = false;
- if (IsBootImage() || Runtime::Current()->UseJit()) {
+ if (IsBootImage() || Runtime::Current()->UseJitCompilation()) {
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<1> hs(soa.Self());
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
@@ -1319,7 +1321,7 @@
result = true;
} else {
// Just check whether the dex cache already has the string.
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
result = (dex_cache->GetResolvedString(string_idx) != nullptr);
}
}
@@ -1427,7 +1429,7 @@
} else {
return false;
}
- } else if (runtime->UseJit() && !heap->IsMovableObject(resolved_class)) {
+ } else if (runtime->UseJitCompilation() && !heap->IsMovableObject(resolved_class)) {
*is_type_initialized = resolved_class->IsInitialized();
// If the class may move around, then don't embed it as a direct pointer.
*use_direct_type_ptr = true;
@@ -1604,7 +1606,7 @@
}
}
}
- if (runtime->UseJit()) {
+ if (runtime->UseJitCompilation()) {
// If we are the JIT, then don't allow a direct call to the interpreter bridge since this will
// never be updated even after we compile the method.
if (cl->IsQuickToInterpreterBridge(
@@ -1636,7 +1638,7 @@
bool must_use_direct_pointers = false;
mirror::DexCache* dex_cache = declaring_class->GetDexCache();
if (target_method->dex_file == dex_cache->GetDexFile() &&
- !(runtime->UseJit() && dex_cache->GetResolvedMethod(
+ !(runtime->UseJitCompilation() && dex_cache->GetResolvedMethod(
method->GetDexMethodIndex(), pointer_size) == nullptr)) {
target_method->dex_method_index = method->GetDexMethodIndex();
} else {
@@ -1673,7 +1675,7 @@
break;
}
}
- if (method_in_image || compiling_boot || runtime->UseJit()) {
+ if (method_in_image || compiling_boot || runtime->UseJitCompilation()) {
// We know we must be able to get to the method in the image, so use that pointer.
// In the case where we are the JIT, we can always use direct pointers since we know where
// the method and its code are / will be. We don't sharpen to interpreter bridge since we
@@ -2440,9 +2442,12 @@
context.ForAll(0, dex_file.NumClassDefs(), &visitor, init_thread_count);
}
-class InitializeArrayClassVisitor : public ClassVisitor {
+class InitializeArrayClassesAndCreateConflictTablesVisitor : public ClassVisitor {
public:
virtual bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ return true;
+ }
if (klass->IsArrayClass()) {
StackHandleScope<1> hs(Thread::Current());
Runtime::Current()->GetClassLinker()->EnsureInitialized(hs.Self(),
@@ -2450,6 +2455,10 @@
true,
true);
}
+ // Create the conflict tables.
+ if (!klass->IsTemp() && klass->ShouldHaveEmbeddedImtAndVTable()) {
+ Runtime::Current()->GetClassLinker()->FillIMTAndConflictTables(klass);
+ }
return true;
}
};
@@ -2462,13 +2471,15 @@
CHECK(dex_file != nullptr);
InitializeClasses(class_loader, *dex_file, dex_files, timings);
}
- {
+ if (boot_image_ || app_image_) {
// Make sure that we call EnsureIntiailized on all the array classes to call
// SetVerificationAttempted so that the access flags are set. If we do not do this they get
// changed at runtime resulting in more dirty image pages.
+ // Also create conflict tables.
+ // Only useful if we are compiling an image (image_classes_ is not null).
ScopedObjectAccess soa(Thread::Current());
- InitializeArrayClassVisitor visitor;
- Runtime::Current()->GetClassLinker()->VisitClasses(&visitor);
+ InitializeArrayClassesAndCreateConflictTablesVisitor visitor;
+ Runtime::Current()->GetClassLinker()->VisitClassesWithoutClassesLock(&visitor);
}
if (IsBootImage()) {
// Prune garbage objects created during aborted transactions.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index d63dffa..19a1ecc 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -92,6 +92,7 @@
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
bool boot_image,
+ bool app_image,
std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
@@ -652,6 +653,7 @@
size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_);
const bool boot_image_;
+ const bool app_image_;
// If image_ is true, specifies the classes that will be included in the image.
// Note if image_classes_ is null, all classes are included in the image.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 8bb462c..00ff522 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -653,8 +653,7 @@
for (ImageInfo& image_info : image_infos_) {
ImageSection unused_sections[ImageHeader::kSectionCount];
const size_t length = RoundUp(
- image_info.CreateImageSections(target_ptr_size_, unused_sections),
- kPageSize);
+ image_info.CreateImageSections(unused_sections), kPageSize);
std::string error_msg;
image_info.image_.reset(MemMap::MapAnonymous("image writer image",
@@ -1214,6 +1213,20 @@
AssignMethodOffset(&m, type, oat_index);
}
(any_dirty ? dirty_methods_ : clean_methods_) += num_methods;
+
+ // Assign offsets for all runtime methods in the IMT since these may hold conflict tables
+ // live.
+ if (as_klass->ShouldHaveEmbeddedImtAndVTable()) {
+ for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+ ArtMethod* imt_method = as_klass->GetEmbeddedImTableEntry(i, target_ptr_size_);
+ DCHECK(imt_method != nullptr);
+ if (imt_method->IsRuntimeMethod() &&
+ !IsInBootImage(imt_method) &&
+ !NativeRelocationAssigned(imt_method)) {
+ AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index);
+ }
+ }
+ }
}
} else if (h_obj->IsObjectArray()) {
// Walk elements of an object array.
@@ -1237,13 +1250,37 @@
}
}
+bool ImageWriter::NativeRelocationAssigned(void* ptr) const {
+ return native_object_relocations_.find(ptr) != native_object_relocations_.end();
+}
+
+void ImageWriter::TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index) {
+ // No offset, or already assigned.
+ if (table == nullptr || NativeRelocationAssigned(table)) {
+ return;
+ }
+ CHECK(!IsInBootImage(table));
+ // If the method is a conflict method we also want to assign the conflict table offset.
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ const size_t size = table->ComputeSize(target_ptr_size_);
+ native_object_relocations_.emplace(
+ table,
+ NativeObjectRelocation {
+ oat_index,
+ image_info.bin_slot_sizes_[kBinIMTConflictTable],
+ kNativeObjectRelocationTypeIMTConflictTable});
+ image_info.bin_slot_sizes_[kBinIMTConflictTable] += size;
+}
+
void ImageWriter::AssignMethodOffset(ArtMethod* method,
NativeObjectRelocationType type,
size_t oat_index) {
DCHECK(!IsInBootImage(method));
- auto it = native_object_relocations_.find(method);
- CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned "
+ CHECK(!NativeRelocationAssigned(method)) << "Method " << method << " already assigned "
<< PrettyMethod(method);
+ if (method->IsRuntimeMethod()) {
+ TryAssignConflictTableOffset(method->GetImtConflictTable(target_ptr_size_), oat_index);
+ }
ImageInfo& image_info = GetImageInfo(oat_index);
size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)];
native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type });
@@ -1292,8 +1329,7 @@
// know where image_roots is going to end up
image_objects_offset_begin_ = RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment
- // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
- heap->VisitObjects(WalkFieldsCallback, this);
+ const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
// Write the image runtime methods.
image_methods_[ImageHeader::kResolutionMethod] = runtime->GetResolutionMethod();
image_methods_[ImageHeader::kImtConflictMethod] = runtime->GetImtConflictMethod();
@@ -1303,31 +1339,19 @@
runtime->GetCalleeSaveMethod(Runtime::kRefsOnly);
image_methods_[ImageHeader::kRefsAndArgsSaveMethod] =
runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
-
- // Add room for fake length prefixed array for holding the image methods.
- const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean;
- auto it = native_object_relocations_.find(&image_method_array_);
- CHECK(it == native_object_relocations_.end());
- ImageInfo& default_image_info = GetImageInfo(GetDefaultOatIndex());
- size_t& offset =
- default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)];
- if (!compile_app_image_) {
- native_object_relocations_.emplace(&image_method_array_,
- NativeObjectRelocation { GetDefaultOatIndex(), offset, image_method_type });
- }
- size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
- const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize(
- 0, ArtMethod::Size(target_ptr_size_), method_alignment);
- CHECK_ALIGNED_PARAM(array_size, method_alignment);
- offset += array_size;
+ // Visit image methods first to have the main runtime methods in the first image.
for (auto* m : image_methods_) {
CHECK(m != nullptr);
CHECK(m->IsRuntimeMethod());
DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
if (!IsInBootImage(m)) {
- AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, GetDefaultOatIndex());
+ AssignMethodOffset(m, kNativeObjectRelocationTypeRuntimeMethod, GetDefaultOatIndex());
}
}
+
+ // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots.
+ heap->VisitObjects(WalkFieldsCallback, this);
+
// Calculate size of the dex cache arrays slot and prepare offsets.
PrepareDexCacheArraySlots();
@@ -1346,15 +1370,22 @@
for (ImageInfo& image_info : image_infos_) {
size_t bin_offset = image_objects_offset_begin_;
for (size_t i = 0; i != kBinSize; ++i) {
+ switch (i) {
+ case kBinArtMethodClean:
+ case kBinArtMethodDirty: {
+ bin_offset = RoundUp(bin_offset, method_alignment);
+ break;
+ }
+ case kBinIMTConflictTable: {
+ bin_offset = RoundUp(bin_offset, target_ptr_size_);
+ break;
+ }
+ default: {
+ // Normal alignment.
+ }
+ }
image_info.bin_slot_offsets_[i] = bin_offset;
bin_offset += image_info.bin_slot_sizes_[i];
- if (i == kBinArtField) {
- static_assert(kBinArtField + 1 == kBinArtMethodClean, "Methods follow fields.");
- static_assert(alignof(ArtField) == 4u, "ArtField alignment is 4.");
- DCHECK_ALIGNED(bin_offset, 4u);
- DCHECK(method_alignment == 4u || method_alignment == 8u);
- bin_offset = RoundUp(bin_offset, method_alignment);
- }
}
// NOTE: There may be additional padding between the bin slots and the intern table.
DCHECK_EQ(image_info.image_end_,
@@ -1367,9 +1398,7 @@
image_info.image_begin_ = global_image_begin_ + image_offset;
image_info.image_offset_ = image_offset;
ImageSection unused_sections[ImageHeader::kSectionCount];
- image_info.image_size_ = RoundUp(
- image_info.CreateImageSections(target_ptr_size_, unused_sections),
- kPageSize);
+ image_info.image_size_ = RoundUp(image_info.CreateImageSections(unused_sections), kPageSize);
// There should be no gaps until the next image.
image_offset += image_info.image_size_;
}
@@ -1396,42 +1425,52 @@
// Note that image_info.image_end_ is left at end of used mirror object section.
}
-size_t ImageWriter::ImageInfo::CreateImageSections(size_t target_ptr_size,
- ImageSection* out_sections) const {
+size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections) const {
DCHECK(out_sections != nullptr);
+
+ // Do not round up any sections here that are represented by the bins since it will break
+ // offsets.
+
// Objects section
- auto* objects_section = &out_sections[ImageHeader::kSectionObjects];
+ ImageSection* objects_section = &out_sections[ImageHeader::kSectionObjects];
*objects_section = ImageSection(0u, image_end_);
- size_t cur_pos = objects_section->End();
+
// Add field section.
- auto* field_section = &out_sections[ImageHeader::kSectionArtFields];
- *field_section = ImageSection(cur_pos, bin_slot_sizes_[kBinArtField]);
+ ImageSection* field_section = &out_sections[ImageHeader::kSectionArtFields];
+ *field_section = ImageSection(bin_slot_offsets_[kBinArtField], bin_slot_sizes_[kBinArtField]);
CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset());
- cur_pos = field_section->End();
- // Round up to the alignment the required by the method section.
- cur_pos = RoundUp(cur_pos, ArtMethod::Alignment(target_ptr_size));
+
// Add method section.
- auto* methods_section = &out_sections[ImageHeader::kSectionArtMethods];
- *methods_section = ImageSection(cur_pos,
- bin_slot_sizes_[kBinArtMethodClean] +
- bin_slot_sizes_[kBinArtMethodDirty]);
- CHECK_EQ(bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset());
- cur_pos = methods_section->End();
+ ImageSection* methods_section = &out_sections[ImageHeader::kSectionArtMethods];
+ *methods_section = ImageSection(
+ bin_slot_offsets_[kBinArtMethodClean],
+ bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinArtMethodDirty]);
+
+ // Conflict tables section.
+ ImageSection* imt_conflict_tables_section = &out_sections[ImageHeader::kSectionIMTConflictTables];
+ *imt_conflict_tables_section = ImageSection(bin_slot_offsets_[kBinIMTConflictTable],
+ bin_slot_sizes_[kBinIMTConflictTable]);
+
+ // Runtime methods section.
+ ImageSection* runtime_methods_section = &out_sections[ImageHeader::kSectionRuntimeMethods];
+ *runtime_methods_section = ImageSection(bin_slot_offsets_[kBinRuntimeMethod],
+ bin_slot_sizes_[kBinRuntimeMethod]);
+
// Add dex cache arrays section.
- auto* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays];
- *dex_cache_arrays_section = ImageSection(cur_pos, bin_slot_sizes_[kBinDexCacheArray]);
- CHECK_EQ(bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset());
- cur_pos = dex_cache_arrays_section->End();
+ ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays];
+ *dex_cache_arrays_section = ImageSection(bin_slot_offsets_[kBinDexCacheArray],
+ bin_slot_sizes_[kBinDexCacheArray]);
+
// Round up to the alignment the string table expects. See HashSet::WriteToMemory.
- cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
+ size_t cur_pos = RoundUp(dex_cache_arrays_section->End(), sizeof(uint64_t));
// Calculate the size of the interned strings.
- auto* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings];
+ ImageSection* interned_strings_section = &out_sections[ImageHeader::kSectionInternedStrings];
*interned_strings_section = ImageSection(cur_pos, intern_table_bytes_);
cur_pos = interned_strings_section->End();
// Round up to the alignment the class table expects. See HashSet::WriteToMemory.
cur_pos = RoundUp(cur_pos, sizeof(uint64_t));
// Calculate the size of the class table section.
- auto* class_table_section = &out_sections[ImageHeader::kSectionClassTable];
+ ImageSection* class_table_section = &out_sections[ImageHeader::kSectionClassTable];
*class_table_section = ImageSection(cur_pos, class_table_bytes_);
cur_pos = class_table_section->End();
// Image end goes right before the start of the image bitmap.
@@ -1446,7 +1485,7 @@
// Create the image sections.
ImageSection sections[ImageHeader::kSectionCount];
- const size_t image_end = image_info.CreateImageSections(target_ptr_size_, sections);
+ const size_t image_end = image_info.CreateImageSections(sections);
// Finally bitmap section.
const size_t bitmap_bytes = image_info.image_bitmap_->Size();
@@ -1531,8 +1570,20 @@
ImageWriter* const image_writer_;
};
+void ImageWriter::CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy) {
+ const size_t count = orig->NumEntries(target_ptr_size_);
+ for (size_t i = 0; i < count; ++i) {
+ ArtMethod* interface_method = orig->GetInterfaceMethod(i, target_ptr_size_);
+ ArtMethod* implementation_method = orig->GetImplementationMethod(i, target_ptr_size_);
+ copy->SetInterfaceMethod(i, target_ptr_size_, NativeLocationInImage(interface_method));
+ copy->SetImplementationMethod(i,
+ target_ptr_size_,
+ NativeLocationInImage(implementation_method));
+ }
+}
+
void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
- ImageInfo& image_info = GetImageInfo(oat_index);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
// Copy ArtFields and methods to their locations and update the array for convenience.
for (auto& pair : native_object_relocations_) {
NativeObjectRelocation& relocation = pair.second;
@@ -1550,6 +1601,7 @@
GetImageAddress(reinterpret_cast<ArtField*>(pair.first)->GetDeclaringClass()));
break;
}
+ case kNativeObjectRelocationTypeRuntimeMethod:
case kNativeObjectRelocationTypeArtMethodClean:
case kNativeObjectRelocationTypeArtMethodDirty: {
CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first),
@@ -1575,26 +1627,22 @@
case kNativeObjectRelocationTypeDexCacheArray:
// Nothing to copy here, everything is done in FixupDexCache().
break;
+ case kNativeObjectRelocationTypeIMTConflictTable: {
+ auto* orig_table = reinterpret_cast<ImtConflictTable*>(pair.first);
+ CopyAndFixupImtConflictTable(
+ orig_table,
+ new(dest)ImtConflictTable(orig_table->NumEntries(target_ptr_size_), target_ptr_size_));
+ break;
+ }
}
}
// Fixup the image method roots.
auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
- const ImageSection& methods_section = image_header->GetMethodsSection();
for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) {
ArtMethod* method = image_methods_[i];
CHECK(method != nullptr);
- // Only place runtime methods in the image of the default oat file.
- if (method->IsRuntimeMethod() && oat_index != GetDefaultOatIndex()) {
- continue;
- }
if (!IsInBootImage(method)) {
- auto it = native_object_relocations_.find(method);
- CHECK(it != native_object_relocations_.end()) << "No forwarding for " << PrettyMethod(method);
- NativeObjectRelocation& relocation = it->second;
- CHECK(methods_section.Contains(relocation.offset)) << relocation.offset << " not in "
- << methods_section;
- CHECK(relocation.IsArtMethodRelocation()) << relocation.type;
- method = reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset);
+ method = NativeLocationInImage(method);
}
image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method);
}
@@ -2057,24 +2105,28 @@
// The resolution method has a special trampoline to call.
Runtime* runtime = Runtime::Current();
- if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
- copy->SetEntryPointFromQuickCompiledCodePtrSize(
- GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_);
- } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() ||
- orig == runtime->GetImtUnimplementedMethod())) {
- copy->SetEntryPointFromQuickCompiledCodePtrSize(
- GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_);
- } else if (UNLIKELY(orig->IsRuntimeMethod())) {
- bool found_one = false;
- for (size_t i = 0; i < static_cast<size_t>(Runtime::kLastCalleeSaveType); ++i) {
- auto idx = static_cast<Runtime::CalleeSaveType>(i);
- if (runtime->HasCalleeSaveMethod(idx) && runtime->GetCalleeSaveMethod(idx) == orig) {
- found_one = true;
- break;
+ if (orig->IsRuntimeMethod()) {
+ ImtConflictTable* orig_table = orig->GetImtConflictTable(target_ptr_size_);
+ if (orig_table != nullptr) {
+ // Special IMT conflict method, normal IMT conflict method or unimplemented IMT method.
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(
+ GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_);
+ copy->SetImtConflictTable(NativeLocationInImage(orig_table), target_ptr_size_);
+ } else if (UNLIKELY(orig == runtime->GetResolutionMethod())) {
+ copy->SetEntryPointFromQuickCompiledCodePtrSize(
+ GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_);
+ } else {
+ bool found_one = false;
+ for (size_t i = 0; i < static_cast<size_t>(Runtime::kLastCalleeSaveType); ++i) {
+ auto idx = static_cast<Runtime::CalleeSaveType>(i);
+ if (runtime->HasCalleeSaveMethod(idx) && runtime->GetCalleeSaveMethod(idx) == orig) {
+ found_one = true;
+ break;
+ }
}
+ CHECK(found_one) << "Expected to find callee save method but got " << PrettyMethod(orig);
+ CHECK(copy->IsRuntimeMethod());
}
- CHECK(found_one) << "Expected to find callee save method but got " << PrettyMethod(orig);
- CHECK(copy->IsRuntimeMethod());
} else {
// We assume all methods have code. If they don't currently then we set them to the use the
// resolution trampoline. Abstract methods never have code and so we need to make sure their
@@ -2141,6 +2193,10 @@
return kBinArtMethodDirty;
case kNativeObjectRelocationTypeDexCacheArray:
return kBinDexCacheArray;
+ case kNativeObjectRelocationTypeRuntimeMethod:
+ return kBinRuntimeMethod;
+ case kNativeObjectRelocationTypeIMTConflictTable:
+ return kBinIMTConflictTable;
}
UNREACHABLE();
}
@@ -2242,7 +2298,6 @@
compile_app_image_(compile_app_image),
target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
image_infos_(oat_filenames.size()),
- image_method_array_(ImageHeader::kImageMethodsCount),
dirty_methods_(0u),
clean_methods_(0u),
image_storage_mode_(image_storage_mode),
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 0cb6aea..51976c5 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -169,6 +169,10 @@
// ArtMethods may be dirty if the class has native methods or a declaring class that isn't
// initialized.
kBinArtMethodDirty,
+ // Conflict tables (clean).
+ kBinIMTConflictTable,
+ // Runtime methods (always clean, do not have a length prefix array).
+ kBinRuntimeMethod,
// Dex cache arrays have a special slot for PC-relative addressing. Since they are
// huge, and as such their dirtiness is not important for the clean/dirty separation,
// we arbitrarily keep them at the end of the native data.
@@ -186,6 +190,8 @@
kNativeObjectRelocationTypeArtMethodArrayClean,
kNativeObjectRelocationTypeArtMethodDirty,
kNativeObjectRelocationTypeArtMethodArrayDirty,
+ kNativeObjectRelocationTypeRuntimeMethod,
+ kNativeObjectRelocationTypeIMTConflictTable,
kNativeObjectRelocationTypeDexCacheArray,
};
friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type);
@@ -240,7 +246,7 @@
// Create the image sections into the out sections variable, returns the size of the image
// excluding the bitmap.
- size_t CreateImageSections(size_t target_ptr_size, ImageSection* out_sections) const;
+ size_t CreateImageSections(ImageSection* out_sections) const;
std::unique_ptr<MemMap> image_; // Memory mapped for generating the image.
@@ -395,6 +401,8 @@
void CopyAndFixupObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info)
SHARED_REQUIRES(Locks::mutator_lock_);
+ void CopyAndFixupImtConflictTable(ImtConflictTable* orig, ImtConflictTable* copy)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void FixupClass(mirror::Class* orig, mirror::Class* copy)
SHARED_REQUIRES(Locks::mutator_lock_);
void FixupObject(mirror::Object* orig, mirror::Object* copy)
@@ -425,6 +433,11 @@
size_t oat_index)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Assign the offset for an IMT conflict table. Does nothing if the table already has a native
+ // relocation.
+ void TryAssignConflictTableOffset(ImtConflictTable* table, size_t oat_index)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Return true if klass is loaded by the boot class loader but not in the boot image.
bool IsBootClassLoaderNonImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -481,6 +494,9 @@
// remove duplicates in the multi image and app image case.
mirror::String* FindInternedString(mirror::String* string) SHARED_REQUIRES(Locks::mutator_lock_);
+ // Return true if there already exists a native allocation for an object.
+ bool NativeRelocationAssigned(void* ptr) const;
+
const CompilerDriver& compiler_driver_;
// Beginning target image address for the first image.
@@ -517,16 +533,14 @@
bool IsArtMethodRelocation() const {
return type == kNativeObjectRelocationTypeArtMethodClean ||
- type == kNativeObjectRelocationTypeArtMethodDirty;
+ type == kNativeObjectRelocationTypeArtMethodDirty ||
+ type == kNativeObjectRelocationTypeRuntimeMethod;
}
};
std::unordered_map<void*, NativeObjectRelocation> native_object_relocations_;
// Runtime ArtMethods which aren't reachable from any Class but need to be copied into the image.
ArtMethod* image_methods_[ImageHeader::kImageMethodsCount];
- // Fake length prefixed array for image methods. This array does not contain the actual
- // ArtMethods. We only use it for the header and relocation addresses.
- LengthPrefixedArray<ArtMethod> image_method_array_;
// Counters for measurements, used for logging only.
uint64_t dirty_methods_;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 5de9842..c2d7ff7 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -155,7 +155,8 @@
Compiler::kOptimizing,
instruction_set,
instruction_set_features_.get(),
- /* image */ false,
+ /* boot_image */ false,
+ /* app_image */ false,
/* image_classes */ nullptr,
/* compiled_classes */ nullptr,
/* compiled_methods */ nullptr,
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index cf836a9..251dc39 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -31,6 +31,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
#include "mirror/stack_trace_element.h"
+#include "nativeloader/native_loader.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
@@ -53,6 +54,11 @@
check_generic_jni_ = false;
}
+ void TearDown() OVERRIDE {
+ android::ResetNativeLoader();
+ CommonCompilerTest::TearDown();
+ }
+
void SetCheckGenericJni(bool generic) {
check_generic_jni_ = generic;
}
@@ -93,10 +99,12 @@
// Start runtime.
Thread::Current()->TransitionFromSuspendedToRunnable();
bool started = runtime_->Start();
+ android::InitializeNativeLoader();
CHECK(started);
}
// JNI operations after runtime start.
env_ = Thread::Current()->GetJniEnv();
+ library_search_path_ = env_->NewStringUTF("");
jklass_ = env_->FindClass("MyClassNatives");
ASSERT_TRUE(jklass_ != nullptr) << method_name << " " << method_sig;
@@ -168,6 +176,7 @@
void StackArgsSignExtendedMips64Impl();
JNIEnv* env_;
+ jstring library_search_path_;
jmethodID jmethod_;
bool check_generic_jni_;
};
@@ -220,7 +229,7 @@
std::string reason;
ASSERT_TRUE(Runtime::Current()->GetJavaVM()->
- LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason))
+ LoadNativeLibrary(env_, "", class_loader_, library_search_path_, &reason))
<< reason;
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
@@ -235,7 +244,7 @@
std::string reason;
ASSERT_TRUE(Runtime::Current()->GetJavaVM()->
- LoadNativeLibrary(env_, "", class_loader_, nullptr, &reason))
+ LoadNativeLibrary(env_, "", class_loader_, library_search_path_, &reason))
<< reason;
jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index c07de79..ec69107 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -51,6 +51,7 @@
instruction_set,
/* instruction_set_features*/ nullptr,
/* boot_image */ false,
+ /* app_image */ false,
/* image_classes */ nullptr,
/* compiled_classes */ nullptr,
/* compiled_methods */ nullptr,
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 73b16d5..5b19284 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -112,6 +112,7 @@
insn_set,
insn_features_.get(),
/* boot_image */ false,
+ /* app_image */ false,
/* image_classes */ nullptr,
/* compiled_classes */ nullptr,
/* compiled_methods */ nullptr,
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index e804bee..8da9f06 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1127,17 +1127,23 @@
return target_offset;
}
- mirror::Class* GetTargetType(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
- mirror::DexCache* dex_cache = (dex_file_ == patch.TargetTypeDexFile())
+ mirror::DexCache* GetDexCache(const DexFile* target_dex_file)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return (target_dex_file == dex_file_)
? dex_cache_
- : class_linker_->FindDexCache(Thread::Current(), *patch.TargetTypeDexFile());
+ : class_linker_->FindDexCache(Thread::Current(), *target_dex_file);
+ }
+
+ mirror::Class* GetTargetType(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::DexCache* dex_cache = GetDexCache(patch.TargetTypeDexFile());
mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
CHECK(type != nullptr);
return type;
}
mirror::String* GetTargetString(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
- mirror::String* string = dex_cache_->GetResolvedString(patch.TargetStringIndex());
+ mirror::DexCache* dex_cache = GetDexCache(patch.TargetStringDexFile());
+ mirror::String* string = dex_cache->GetResolvedString(patch.TargetStringIndex());
DCHECK(string != nullptr);
DCHECK(writer_->HasBootImage() ||
Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string));
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 659c6f8..6c6e5af 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -552,7 +552,11 @@
DCHECK(!IsAddedBlock(block));
first_index_bounds_check_map_.clear();
HGraphVisitor::VisitBasicBlock(block);
- AddComparesWithDeoptimization(block);
+ // We should never deoptimize from an osr method, otherwise we might wrongly optimize
+ // code dominated by the deoptimization.
+ if (!GetGraph()->IsCompilingOsr()) {
+ AddComparesWithDeoptimization(block);
+ }
}
void Finish() {
@@ -796,6 +800,27 @@
ValueRange(GetGraph()->GetArena(), ValueBound::Min(), new_upper);
ApplyRangeFromComparison(left, block, false_successor, new_range);
}
+ } else if (cond == kCondNE || cond == kCondEQ) {
+ if (left->IsArrayLength() && lower.IsConstant() && upper.IsConstant()) {
+ // Special case:
+ // length == [c,d] yields [c, d] along true
+ // length != [c,d] yields [c, d] along false
+ if (!lower.Equals(ValueBound::Min()) || !upper.Equals(ValueBound::Max())) {
+ ValueRange* new_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), lower, upper);
+ ApplyRangeFromComparison(
+ left, block, cond == kCondEQ ? true_successor : false_successor, new_range);
+ }
+ // In addition:
+ // length == 0 yields [1, max] along false
+ // length != 0 yields [1, max] along true
+ if (lower.GetConstant() == 0 && upper.GetConstant() == 0) {
+ ValueRange* new_range = new (GetGraph()->GetArena())
+ ValueRange(GetGraph()->GetArena(), ValueBound(nullptr, 1), ValueBound::Max());
+ ApplyRangeFromComparison(
+ left, block, cond == kCondEQ ? false_successor : true_successor, new_range);
+ }
+ }
}
}
@@ -951,13 +976,7 @@
void VisitIf(HIf* instruction) OVERRIDE {
if (instruction->InputAt(0)->IsCondition()) {
HCondition* cond = instruction->InputAt(0)->AsCondition();
- IfCondition cmp = cond->GetCondition();
- if (cmp == kCondGT || cmp == kCondGE ||
- cmp == kCondLT || cmp == kCondLE) {
- HInstruction* left = cond->GetLeft();
- HInstruction* right = cond->GetRight();
- HandleIf(instruction, left, right, cmp);
- }
+ HandleIf(instruction, cond->GetLeft(), cond->GetRight(), cond->GetCondition());
}
}
@@ -1358,6 +1377,11 @@
if (loop->IsIrreducible()) {
return false;
}
+ // We should never deoptimize from an osr method, otherwise we might wrongly optimize
+ // code dominated by the deoptimization.
+ if (GetGraph()->IsCompilingOsr()) {
+ return false;
+ }
// A try boundary preheader is hard to handle.
// TODO: remove this restriction.
if (loop->GetPreHeader()->GetLastInstruction()->IsTryBoundary()) {
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 45d23fe..197e473 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5183,10 +5183,10 @@
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
// TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index e8e6b68..9680f2b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4010,10 +4010,10 @@
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index e73e880..6dc480b 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3303,17 +3303,6 @@
int shift;
CalculateMagicAndShiftForDivRem(imm, false /* is_long */, &magic, &shift);
- NearLabel ndiv;
- NearLabel end;
- // If numerator is 0, the result is 0, no computation needed.
- __ testl(eax, eax);
- __ j(kNotEqual, &ndiv);
-
- __ xorl(out, out);
- __ jmp(&end);
-
- __ Bind(&ndiv);
-
// Save the numerator.
__ movl(num, eax);
@@ -3348,7 +3337,6 @@
} else {
__ movl(eax, edx);
}
- __ Bind(&end);
}
void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instruction) {
@@ -5977,7 +5965,7 @@
DCHECK(GetCompilerOptions().GetCompilePic());
FALLTHROUGH_INTENDED;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit()); // Note: boot image is also non-JIT.
+ DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT.
// We disable pc-relative load when there is an irreducible loop, as the optimization
// is incompatible with it.
// TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods
@@ -5989,7 +5977,7 @@
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 5576d83..96ec09c 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3390,16 +3390,6 @@
__ movl(numerator, eax);
- NearLabel no_div;
- NearLabel end;
- __ testl(eax, eax);
- __ j(kNotEqual, &no_div);
-
- __ xorl(out, out);
- __ jmp(&end);
-
- __ Bind(&no_div);
-
__ movl(eax, Immediate(magic));
__ imull(numerator);
@@ -3425,7 +3415,6 @@
} else {
__ movl(eax, edx);
}
- __ Bind(&end);
} else {
int64_t imm = second.GetConstant()->AsLongConstant()->GetValue();
@@ -5413,10 +5402,10 @@
case HLoadString::LoadKind::kBootImageAddress:
break;
case HLoadString::LoadKind::kDexCacheAddress:
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCachePcRelative:
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
break;
case HLoadString::LoadKind::kDexCacheViaMethod:
break;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 96837a8..968e267 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -258,6 +258,15 @@
VisitInstruction(check);
}
+void GraphChecker::VisitDeoptimize(HDeoptimize* deopt) {
+ if (GetGraph()->IsCompilingOsr()) {
+ AddError(StringPrintf("A graph compiled OSR cannot have a HDeoptimize instruction"));
+ }
+
+ // Perform the instruction base checks too.
+ VisitInstruction(deopt);
+}
+
void GraphChecker::VisitTryBoundary(HTryBoundary* try_boundary) {
ArrayRef<HBasicBlock* const> handlers = try_boundary->GetExceptionHandlers();
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 83b1984..3060c80 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -57,6 +57,7 @@
void VisitCheckCast(HCheckCast* check) OVERRIDE;
void VisitCondition(HCondition* op) OVERRIDE;
void VisitConstant(HConstant* instruction) OVERRIDE;
+ void VisitDeoptimize(HDeoptimize* instruction) OVERRIDE;
void VisitIf(HIf* instruction) OVERRIDE;
void VisitInstanceOf(HInstanceOf* check) OVERRIDE;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 836bcfa..59de895 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -308,7 +308,7 @@
// Check if we can use an inline cache.
ArtMethod* caller = graph_->GetArtMethod();
- if (Runtime::Current()->UseJit()) {
+ if (Runtime::Current()->UseJitCompilation()) {
// Under JIT, we should always know the caller.
DCHECK(caller != nullptr);
ScopedProfilingInfoInlineUse spiis(caller, soa.Self());
@@ -322,7 +322,13 @@
return false;
} else if (ic.IsMonomorphic()) {
MaybeRecordStat(kMonomorphicCall);
- return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
+ if (outermost_graph_->IsCompilingOsr()) {
+ // If we are compiling OSR, we pretend this call is polymorphic, as we may come from the
+ // interpreter and it may have seen different receiver types.
+ return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
+ } else {
+ return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
+ }
} else if (ic.IsPolymorphic()) {
MaybeRecordStat(kPolymorphicCall);
return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
@@ -510,6 +516,11 @@
bool deoptimize = all_targets_inlined &&
(i != InlineCache::kIndividualCacheSize - 1) &&
(ic.GetTypeAt(i + 1) == nullptr);
+
+ if (outermost_graph_->IsCompilingOsr()) {
+ // We do not support HDeoptimize in OSR methods.
+ deoptimize = false;
+ }
HInstruction* compare = AddTypeGuard(
receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
if (deoptimize) {
@@ -623,7 +634,7 @@
ArtMethod* resolved_method,
const InlineCache& ic) {
// This optimization only works under JIT for now.
- DCHECK(Runtime::Current()->UseJit());
+ DCHECK(Runtime::Current()->UseJitCompilation());
if (graph_->GetInstructionSet() == kMips64) {
// TODO: Support HClassTableGet for mips64.
return false;
@@ -672,7 +683,8 @@
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ false)) {
+ HInstruction* return_replacement = nullptr;
+ if (!TryBuildAndInline(invoke_instruction, actual_method, &return_replacement)) {
return false;
}
@@ -701,9 +713,6 @@
}
HNotEqual* compare = new (graph_->GetArena()) HNotEqual(class_table_get, constant);
- HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
- compare, invoke_instruction->GetDexPc());
- // TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(receiver_class, cursor);
} else {
@@ -711,8 +720,20 @@
}
bb_cursor->InsertInstructionAfter(class_table_get, receiver_class);
bb_cursor->InsertInstructionAfter(compare, class_table_get);
- bb_cursor->InsertInstructionAfter(deoptimize, compare);
- deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+
+ if (outermost_graph_->IsCompilingOsr()) {
+ CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
+ } else {
+ // TODO: Extend reference type propagation to understand the guard.
+ HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+ compare, invoke_instruction->GetDexPc());
+ bb_cursor->InsertInstructionAfter(deoptimize, compare);
+ deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+ }
// Run type propagation to get the guard typed.
ReferenceTypePropagation rtp_fixup(graph_,
@@ -744,6 +765,12 @@
HInstruction** return_replacement) {
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ if (method->IsProxyMethod()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method)
+ << " is not inlined because of unimplemented inline support for proxy methods.";
+ return false;
+ }
+
// Check whether we're allowed to inline. The outermost compilation unit is the relevant
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
@@ -795,9 +822,14 @@
return false;
}
+ if (!method->IsCompilable()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method)
+ << " has soft failures un-handled by the compiler, so it cannot be inlined";
+ }
+
if (!method->GetDeclaringClass()->IsVerified()) {
uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
- if (Runtime::Current()->UseJit() ||
+ if (Runtime::Current()->UseJitCompilation() ||
!compiler_driver_->IsMethodVerifiedWithoutFailures(
method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
@@ -1260,6 +1292,8 @@
size_t HInliner::RunOptimizations(HGraph* callee_graph,
const DexFile::CodeItem* code_item,
const DexCompilationUnit& dex_compilation_unit) {
+ // Note: if the outermost_graph_ is being compiled OSR, we should not run any
+ // optimization that could lead to a HDeoptimize. The following optimizations do not.
HDeadCodeElimination dce(callee_graph, stats_);
HConstantFolding fold(callee_graph);
HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 12cb826..a834216 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -215,6 +215,17 @@
}
}
+HInstruction* HInstructionBuilder::LoadNullCheckedLocal(uint32_t register_index, uint32_t dex_pc) {
+ HInstruction* ref = LoadLocal(register_index, Primitive::kPrimNot);
+ if (!ref->CanBeNull()) {
+ return ref;
+ }
+
+ HNullCheck* null_check = new (arena_) HNullCheck(ref, dex_pc);
+ AppendInstruction(null_check);
+ return null_check;
+}
+
void HInstructionBuilder::SetLoopHeaderPhiInputs() {
for (size_t i = loop_headers_.size(); i > 0; --i) {
HBasicBlock* block = loop_headers_[i - 1];
@@ -662,6 +673,16 @@
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
+ // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
+ // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
+ Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod(
+ method_idx, dex_compilation_unit_->GetDexCache(), class_loader)));
+
+ if (UNLIKELY(methods_class.Get() == nullptr)) {
+ // Clean up any exception left by type resolution.
+ soa.Self()->ClearException();
+ return nullptr;
+ }
ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
*dex_compilation_unit_->GetDexFile(),
@@ -700,47 +721,33 @@
DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
}
- ArtMethod* current_method = graph_->GetArtMethod();
- DCHECK(current_method != nullptr);
- Handle<mirror::Class> methods_class(hs.NewHandle(
- dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
- method_idx,
- current_method)));
- if (methods_class.Get() == nullptr) {
- // Invoking a super method requires knowing the actual super class. If we did not resolve
- // the compiling method's declaring class (which only happens for ahead of time
- // compilation), bail out.
- DCHECK(Runtime::Current()->IsAotCompiler());
- return nullptr;
+ ArtMethod* actual_method;
+ if (methods_class->IsInterface()) {
+ actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+ resolved_method, class_linker->GetImagePointerSize());
} else {
- ArtMethod* actual_method;
- if (methods_class->IsInterface()) {
- actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
- resolved_method, class_linker->GetImagePointerSize());
- } else {
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
- vtable_index, class_linker->GetImagePointerSize());
- }
- if (actual_method != resolved_method &&
- !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
- // The back-end code generator relies on this check in order to ensure that it will not
- // attempt to read the dex_cache with a dex_method_index that is not from the correct
- // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
- // builder, which means that the code-generator (and compiler driver during sharpening and
- // inliner, maybe) might invoke an incorrect method.
- // TODO: The actual method could still be referenced in the current dex file, so we
- // could try locating it.
- // TODO: Remove the dex_file restriction.
- return nullptr;
- }
- if (!actual_method->IsInvokable()) {
- // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
- // could resolve the callee to the wrong method.
- return nullptr;
- }
- resolved_method = actual_method;
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
+ vtable_index, class_linker->GetImagePointerSize());
}
+ if (actual_method != resolved_method &&
+ !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ // The back-end code generator relies on this check in order to ensure that it will not
+ // attempt to read the dex_cache with a dex_method_index that is not from the correct
+ // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
+ // builder, which means that the code-generator (and compiler driver during sharpening and
+ // inliner, maybe) might invoke an incorrect method.
+ // TODO: The actual method could still be referenced in the current dex file, so we
+ // could try locating it.
+ // TODO: Remove the dex_file restriction.
+ return nullptr;
+ }
+ if (!actual_method->IsInvokable()) {
+ // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
+ // could resolve the callee to the wrong method.
+ return nullptr;
+ }
+ resolved_method = actual_method;
}
// Check for incompatible class changes. The class linker has a fast path for
@@ -1084,10 +1091,9 @@
size_t start_index = 0;
size_t argument_index = 0;
if (invoke->GetOriginalInvokeType() != InvokeType::kStatic) { // Instance call.
- HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
- HNullCheck* null_check = new (arena_) HNullCheck(arg, invoke->GetDexPc());
- AppendInstruction(null_check);
- invoke->SetArgumentAt(0, null_check);
+ HInstruction* arg = LoadNullCheckedLocal(is_range ? register_index : args[0],
+ invoke->GetDexPc());
+ invoke->SetArgumentAt(0, arg);
start_index = 1;
argument_index = 1;
}
@@ -1193,9 +1199,7 @@
compiler_driver_->ComputeInstanceFieldInfo(field_index, dex_compilation_unit_, is_put, soa);
- HInstruction* object = LoadLocal(obj_reg, Primitive::kPrimNot);
- HInstruction* null_check = new (arena_) HNullCheck(object, dex_pc);
- AppendInstruction(null_check);
+ HInstruction* object = LoadNullCheckedLocal(obj_reg, dex_pc);
Primitive::Type field_type = (resolved_field == nullptr)
? GetFieldAccessType(*dex_file_, field_index)
@@ -1205,14 +1209,14 @@
HInstruction* field_set = nullptr;
if (resolved_field == nullptr) {
MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- field_set = new (arena_) HUnresolvedInstanceFieldSet(null_check,
+ field_set = new (arena_) HUnresolvedInstanceFieldSet(object,
value,
field_type,
field_index,
dex_pc);
} else {
uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
- field_set = new (arena_) HInstanceFieldSet(null_check,
+ field_set = new (arena_) HInstanceFieldSet(object,
value,
field_type,
resolved_field->GetOffset(),
@@ -1228,13 +1232,13 @@
HInstruction* field_get = nullptr;
if (resolved_field == nullptr) {
MaybeRecordStat(MethodCompilationStat::kUnresolvedField);
- field_get = new (arena_) HUnresolvedInstanceFieldGet(null_check,
+ field_get = new (arena_) HUnresolvedInstanceFieldGet(object,
field_type,
field_index,
dex_pc);
} else {
uint16_t class_def_index = resolved_field->GetDeclaringClass()->GetDexClassDefIndex();
- field_get = new (arena_) HInstanceFieldGet(null_check,
+ field_get = new (arena_) HInstanceFieldGet(object,
field_type,
resolved_field->GetOffset(),
resolved_field->IsVolatile(),
@@ -1449,10 +1453,7 @@
uint8_t array_reg = instruction.VRegB_23x();
uint8_t index_reg = instruction.VRegC_23x();
- HInstruction* object = LoadLocal(array_reg, Primitive::kPrimNot);
- object = new (arena_) HNullCheck(object, dex_pc);
- AppendInstruction(object);
-
+ HInstruction* object = LoadNullCheckedLocal(array_reg, dex_pc);
HInstruction* length = new (arena_) HArrayLength(object, dex_pc);
AppendInstruction(length);
HInstruction* index = LoadLocal(index_reg, Primitive::kPrimInt);
@@ -1527,11 +1528,8 @@
}
void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc) {
- HInstruction* array = LoadLocal(instruction.VRegA_31t(), Primitive::kPrimNot);
- HNullCheck* null_check = new (arena_) HNullCheck(array, dex_pc);
- AppendInstruction(null_check);
-
- HInstruction* length = new (arena_) HArrayLength(null_check, dex_pc);
+ HInstruction* array = LoadNullCheckedLocal(instruction.VRegA_31t(), dex_pc);
+ HInstruction* length = new (arena_) HArrayLength(array, dex_pc);
AppendInstruction(length);
int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
@@ -1547,28 +1545,28 @@
switch (payload->element_width) {
case 1:
- BuildFillArrayData(null_check,
+ BuildFillArrayData(array,
reinterpret_cast<const int8_t*>(data),
element_count,
Primitive::kPrimByte,
dex_pc);
break;
case 2:
- BuildFillArrayData(null_check,
+ BuildFillArrayData(array,
reinterpret_cast<const int16_t*>(data),
element_count,
Primitive::kPrimShort,
dex_pc);
break;
case 4:
- BuildFillArrayData(null_check,
+ BuildFillArrayData(array,
reinterpret_cast<const int32_t*>(data),
element_count,
Primitive::kPrimInt,
dex_pc);
break;
case 8:
- BuildFillWideArrayData(null_check,
+ BuildFillWideArrayData(array,
reinterpret_cast<const int64_t*>(data),
element_count,
dex_pc);
@@ -2575,9 +2573,7 @@
ARRAY_XX(_SHORT, Primitive::kPrimShort);
case Instruction::ARRAY_LENGTH: {
- HInstruction* object = LoadLocal(instruction.VRegB_12x(), Primitive::kPrimNot);
- object = new (arena_) HNullCheck(object, dex_pc);
- AppendInstruction(object);
+ HInstruction* object = LoadNullCheckedLocal(instruction.VRegB_12x(), dex_pc);
AppendInstruction(new (arena_) HArrayLength(object, dex_pc));
UpdateLocal(instruction.VRegA_12x(), current_block_->GetLastInstruction());
break;
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 070f7da..0e3e5a7 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -87,6 +87,7 @@
ArenaVector<HInstruction*>* GetLocalsFor(HBasicBlock* block);
HInstruction* ValueOfLocalAt(HBasicBlock* block, size_t local);
HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type) const;
+ HInstruction* LoadNullCheckedLocal(uint32_t register_index, uint32_t dex_pc);
void UpdateLocal(uint32_t register_index, HInstruction* instruction);
void AppendInstruction(HInstruction* instruction);
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 863dd1c..39a1313 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -30,6 +30,10 @@
// Temporary measure until we have caught up with the Java 7 definition of Math.round. b/26327751
static constexpr bool kRoundIsPlusPointFive = false;
+// Positive floating-point infinities.
+static constexpr uint32_t kPositiveInfinityFloat = 0x7f800000U;
+static constexpr uint64_t kPositiveInfinityDouble = UINT64_C(0x7ff0000000000000);
+
// Recognize intrinsics from HInvoke nodes.
class IntrinsicsRecognizer : public HOptimization {
public:
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 86b7bc1..146fea1 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1985,6 +1985,56 @@
__ Bind(&done);
}
+void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitFloatIsInfinite(HInvoke* invoke) {
+ ArmAssembler* const assembler = GetAssembler();
+ LocationSummary* const locations = invoke->GetLocations();
+ const Register out = locations->Out().AsRegister<Register>();
+ // Shifting left by 1 bit makes the value encodable as an immediate operand;
+ // we don't care about the sign bit anyway.
+ constexpr uint32_t infinity = kPositiveInfinityFloat << 1U;
+
+ __ vmovrs(out, locations->InAt(0).AsFpuRegister<SRegister>());
+ // We don't care about the sign bit, so shift left.
+ __ Lsl(out, out, 1);
+ __ eor(out, out, ShifterOperand(infinity));
+ // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
+ __ clz(out, out);
+ // Any number less than 32 logically shifted right by 5 bits results in 0;
+ // the same operation on 32 yields 1.
+ __ Lsr(out, out, 5);
+}
+
+void IntrinsicLocationsBuilderARM::VisitDoubleIsInfinite(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitDoubleIsInfinite(HInvoke* invoke) {
+ ArmAssembler* const assembler = GetAssembler();
+ LocationSummary* const locations = invoke->GetLocations();
+ const Register out = locations->Out().AsRegister<Register>();
+ // The highest 32 bits of double precision positive infinity separated into
+ // two constants encodable as immediate operands.
+ constexpr uint32_t infinity_high = 0x7f000000U;
+ constexpr uint32_t infinity_high2 = 0x00f00000U;
+
+ static_assert((infinity_high | infinity_high2) == static_cast<uint32_t>(kPositiveInfinityDouble >> 32U),
+ "The constants do not add up to the high 32 bits of double precision positive infinity.");
+ __ vmovrrd(IP, out, FromLowSToD(locations->InAt(0).AsFpuRegisterPairLow<SRegister>()));
+ __ eor(out, out, ShifterOperand(infinity_high));
+ __ eor(out, out, ShifterOperand(infinity_high2));
+ // We don't care about the sign bit, so shift left.
+ __ orr(out, IP, ShifterOperand(out, LSL, 1));
+ // If the result is 0, then it has 32 leading zeros, and less than that otherwise.
+ __ clz(out, out);
+ // Any number less than 32 logically shifted right by 5 bits results in 0;
+ // the same operation on 32 yields 1.
+ __ Lsr(out, out, 5);
+}
+
UNIMPLEMENTED_INTRINSIC(ARM, IntegerBitCount)
UNIMPLEMENTED_INTRINSIC(ARM, LongBitCount)
UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble)
@@ -2001,8 +2051,6 @@
UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(ARM, FloatIsInfinite)
-UNIMPLEMENTED_INTRINSIC(ARM, DoubleIsInfinite)
UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 04ae3a6..1d82296 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -47,6 +47,7 @@
using helpers::WRegisterFrom;
using helpers::XRegisterFrom;
using helpers::InputRegisterAt;
+using helpers::OutputRegister;
namespace {
@@ -1173,31 +1174,118 @@
void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCall,
+ invoke->InputAt(1)->CanBeNull()
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall,
kIntrinsified);
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
- locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) {
vixl::MacroAssembler* masm = GetVIXLAssembler();
LocationSummary* locations = invoke->GetLocations();
+ Register str = XRegisterFrom(locations->InAt(0));
+ Register arg = XRegisterFrom(locations->InAt(1));
+ Register out = OutputRegister(invoke);
+
+ Register temp0 = WRegisterFrom(locations->GetTemp(0));
+ Register temp1 = WRegisterFrom(locations->GetTemp(1));
+ Register temp2 = WRegisterFrom(locations->GetTemp(2));
+
+ vixl::Label loop;
+ vixl::Label find_char_diff;
+ vixl::Label end;
+
+ // Get offsets of count and value fields within a string object.
+ const int32_t count_offset = mirror::String::CountOffset().Int32Value();
+ const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+
// Note that the null check must have been done earlier.
DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0)));
- Register argument = WRegisterFrom(locations->InAt(1));
- __ Cmp(argument, 0);
- SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
- codegen_->AddSlowPath(slow_path);
- __ B(eq, slow_path->GetEntryLabel());
+ // Take slow path and throw if input can be and is null.
+ SlowPathCodeARM64* slow_path = nullptr;
+ const bool can_slow_path = invoke->InputAt(1)->CanBeNull();
+ if (can_slow_path) {
+ slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
+ codegen_->AddSlowPath(slow_path);
+ __ Cbz(arg, slow_path->GetEntryLabel());
+ }
- __ Ldr(
- lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pStringCompareTo).Int32Value()));
- __ Blr(lr);
- __ Bind(slow_path->GetExitLabel());
+ // Reference equality check, return 0 if same reference.
+ __ Subs(out, str, arg);
+ __ B(&end, eq);
+ // Load lengths of this and argument strings.
+ __ Ldr(temp0, MemOperand(str.X(), count_offset));
+ __ Ldr(temp1, MemOperand(arg.X(), count_offset));
+ // Return zero if both strings are empty.
+ __ Orr(out, temp0, temp1);
+ __ Cbz(out, &end);
+ // out = length diff.
+ __ Subs(out, temp0, temp1);
+ // temp2 = min(len(str), len(arg)).
+ __ Csel(temp2, temp1, temp0, ge);
+ // Shorter string is empty?
+ __ Cbz(temp2, &end);
+
+ // Store offset of string value in preparation for comparison loop.
+ __ Mov(temp1, value_offset);
+
+ UseScratchRegisterScope scratch_scope(masm);
+ Register temp4 = scratch_scope.AcquireX();
+
+ // Assertions that must hold in order to compare strings 4 characters at a time.
+ DCHECK_ALIGNED(value_offset, 8);
+ static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
+
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+
+ // Promote temp0 to an X reg, ready for LDR.
+ temp0 = temp0.X();
+
+ // Loop to compare 4x16-bit characters at a time (ok because of string data alignment).
+ __ Bind(&loop);
+ __ Ldr(temp4, MemOperand(str.X(), temp1));
+ __ Ldr(temp0, MemOperand(arg.X(), temp1));
+ __ Cmp(temp4, temp0);
+ __ B(ne, &find_char_diff);
+ __ Add(temp1, temp1, char_size * 4);
+ __ Subs(temp2, temp2, 4);
+ __ B(gt, &loop);
+ __ B(&end);
+
+ // Promote temp1 to an X reg, ready for EOR.
+ temp1 = temp1.X();
+
+ // Find the single 16-bit character difference.
+ __ Bind(&find_char_diff);
+ // Get the bit position of the first character that differs.
+ __ Eor(temp1, temp0, temp4);
+ __ Rbit(temp1, temp1);
+ __ Clz(temp1, temp1);
+ __ Bic(temp1, temp1, 0xf);
+ // If the number of 16-bit chars remaining <= the index where the difference occurs (0-3), then
+ // the difference occurs outside the remaining string data, so just return length diff (out).
+ __ Cmp(temp2, Operand(temp1, LSR, 4));
+ __ B(le, &end);
+ // Extract the characters and calculate the difference.
+ __ Lsr(temp0, temp0, temp1);
+ __ Lsr(temp4, temp4, temp1);
+ __ And(temp4, temp4, 0xffff);
+ __ Sub(out, temp4, Operand(temp0, UXTH));
+
+ __ Bind(&end);
+
+ if (can_slow_path) {
+ __ Bind(slow_path->GetExitLabel());
+ }
}
void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) {
@@ -2201,9 +2289,46 @@
__ Bind(slow_path->GetExitLabel());
}
+static void GenIsInfinite(LocationSummary* locations,
+ bool is64bit,
+ vixl::MacroAssembler* masm) {
+ Operand infinity;
+ Register out;
+
+ if (is64bit) {
+ infinity = kPositiveInfinityDouble;
+ out = XRegisterFrom(locations->Out());
+ } else {
+ infinity = kPositiveInfinityFloat;
+ out = WRegisterFrom(locations->Out());
+ }
+
+ const Register zero = vixl::Assembler::AppropriateZeroRegFor(out);
+
+ MoveFPToInt(locations, is64bit, masm);
+ __ Eor(out, out, infinity);
+ // We don't care about the sign bit, so shift left.
+ __ Cmp(zero, Operand(out, LSL, 1));
+ __ Cset(out, eq);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitFloatIsInfinite(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitFloatIsInfinite(HInvoke* invoke) {
+ GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
+ CreateFPToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitDoubleIsInfinite(HInvoke* invoke) {
+ GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetVIXLAssembler());
+}
+
UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(ARM64, FloatIsInfinite)
-UNIMPLEMENTED_INTRINSIC(ARM64, DoubleIsInfinite)
UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 19c6a22..46195c1 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2283,10 +2283,10 @@
// If one, or more, of the exponent bits is zero, then the number can't be infinite.
if (type == Primitive::kPrimDouble) {
__ MoveFromFpuHigh(TMP, in);
- __ LoadConst32(AT, 0x7FF00000);
+ __ LoadConst32(AT, High32Bits(kPositiveInfinityDouble));
} else {
__ Mfc1(TMP, in);
- __ LoadConst32(AT, 0x7F800000);
+ __ LoadConst32(AT, kPositiveInfinityFloat);
}
__ Xor(TMP, TMP, AT);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index cf973aa..1524e1e 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -385,6 +385,92 @@
locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
}
+static void GenBitCount(LocationSummary* locations,
+ const Primitive::Type type,
+ Mips64Assembler* assembler) {
+ GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
+
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ //
+ // A generalization of the best bit counting method to integers of
+ // bit-widths up to 128 (parameterized by type T) is this:
+ //
+ // v = v - ((v >> 1) & (T)~(T)0/3); // temp
+ // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
+ // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
+ // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count
+ //
+ // For comparison, for 32-bit quantities, this algorithm can be executed
+ // using 20 MIPS instructions (the calls to LoadConst32() generate two
+ // machine instructions each for the values being used in this algorithm).
+ // A(n unrolled) loop-based algorithm requires 25 instructions.
+ //
+ // For a 64-bit operand this can be performed in 24 instructions compared
+ // to a(n unrolled) loop based algorithm which requires 38 instructions.
+ //
+ // There are algorithms which are faster in the cases where very few
+ // bits are set but the algorithm here attempts to minimize the total
+ // number of instructions executed even when a large number of bits
+ // are set.
+
+ if (type == Primitive::kPrimInt) {
+ __ Srl(TMP, in, 1);
+ __ LoadConst32(AT, 0x55555555);
+ __ And(TMP, TMP, AT);
+ __ Subu(TMP, in, TMP);
+ __ LoadConst32(AT, 0x33333333);
+ __ And(out, TMP, AT);
+ __ Srl(TMP, TMP, 2);
+ __ And(TMP, TMP, AT);
+ __ Addu(TMP, out, TMP);
+ __ Srl(out, TMP, 4);
+ __ Addu(out, out, TMP);
+ __ LoadConst32(AT, 0x0F0F0F0F);
+ __ And(out, out, AT);
+ __ LoadConst32(TMP, 0x01010101);
+ __ MulR6(out, out, TMP);
+ __ Srl(out, out, 24);
+ } else if (type == Primitive::kPrimLong) {
+ __ Dsrl(TMP, in, 1);
+ __ LoadConst64(AT, 0x5555555555555555L);
+ __ And(TMP, TMP, AT);
+ __ Dsubu(TMP, in, TMP);
+ __ LoadConst64(AT, 0x3333333333333333L);
+ __ And(out, TMP, AT);
+ __ Dsrl(TMP, TMP, 2);
+ __ And(TMP, TMP, AT);
+ __ Daddu(TMP, out, TMP);
+ __ Dsrl(out, TMP, 4);
+ __ Daddu(out, out, TMP);
+ __ LoadConst64(AT, 0x0F0F0F0F0F0F0F0FL);
+ __ And(out, out, AT);
+ __ LoadConst64(TMP, 0x0101010101010101L);
+ __ Dmul(out, out, TMP);
+ __ Dsrl32(out, out, 24);
+ }
+}
+
+// int java.lang.Integer.bitCount(int)
+void IntrinsicLocationsBuilderMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitIntegerBitCount(HInvoke* invoke) {
+ GenBitCount(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+// int java.lang.Long.bitCount(long)
+void IntrinsicLocationsBuilderMIPS64::VisitLongBitCount(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitLongBitCount(HInvoke* invoke) {
+ GenBitCount(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
static void MathAbsFP(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
@@ -1693,9 +1779,6 @@
GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
}
-UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerBitCount)
-UNIMPLEMENTED_INTRINSIC(MIPS64, LongBitCount)
-
UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundFloat)
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fe75451..6703695 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2218,6 +2218,7 @@
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsValidHandle(type_handle));
DCHECK(!type_handle->IsErroneous());
+ DCHECK(!type_handle->IsArrayClass() || !type_handle->GetComponentType()->IsErroneous());
if (!is_exact) {
DCHECK(!type_handle->CannotBeAssignedFromOtherTypes())
<< "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed";
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3670ce2..37197af 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -769,15 +769,6 @@
return codegen.release();
}
-static bool CanHandleVerificationFailure(const VerifiedMethod* verified_method) {
- // For access errors the compiler will use the unresolved helpers (e.g. HInvokeUnresolved).
- uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS
- | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS
- | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD
- | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
- return (verified_method->GetEncounteredVerificationFailures() & (~unresolved_mask)) == 0;
-}
-
CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type,
@@ -792,7 +783,8 @@
const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx);
DCHECK(!verified_method->HasRuntimeThrow());
if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)
- || CanHandleVerificationFailure(verified_method)) {
+ || verifier::MethodVerifier::CanCompilerHandleVerificationFailure(
+ verified_method->GetEncounteredVerificationFailures())) {
ArenaAllocator arena(Runtime::Current()->GetArenaPool());
CodeVectorAllocator code_allocator(&arena);
std::unique_ptr<CodeGenerator> codegen(
@@ -865,6 +857,7 @@
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
method->GetDeclaringClass()->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+ DCHECK(method->IsCompilable());
jobject jclass_loader = class_loader.ToJObject();
const DexFile* dex_file = method->GetDexFile();
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 04c9ff9..f2394f6 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -46,6 +46,13 @@
return *cache;
}
+// Returns true if klass is admissible to the propagation: non-null and non-erroneous.
+// For an array type, we also check if the component type is admissible.
+static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) {
+ return klass != nullptr && !klass->IsErroneous() &&
+ (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType()));
+}
+
ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() {
return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_);
}
@@ -453,15 +460,10 @@
}
instr->SetReferenceTypeInfo(
ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true));
- } else if (klass != nullptr) {
- if (klass->IsErroneous()) {
- // Set inexact object type for erroneous types.
- instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
- } else {
- ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
- is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
- instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
- }
+ } else if (IsAdmissible(klass)) {
+ ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass);
+ is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes();
+ instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact));
} else {
instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti());
}
@@ -563,7 +565,7 @@
instr->GetDexFile(),
instr->GetTypeIndex(),
hint_dex_cache_);
- if (resolved_class != nullptr && !resolved_class->IsErroneous()) {
+ if (IsAdmissible(resolved_class)) {
instr->SetLoadedClassRTI(ReferenceTypeInfo::Create(
handle_cache_->NewHandle(resolved_class), /* is_exact */ true));
}
@@ -664,12 +666,6 @@
}
if (phi->GetBlock()->IsLoopHeader()) {
- if (!is_first_run_ && graph_->IsCompilingOsr()) {
- // Don't update the type of a loop phi when compiling OSR: we may have done
- // speculative optimizations dominating that phi, that do not hold at the
- // point the interpreter jumps to that loop header.
- return;
- }
// Set the initial type for the phi. Use the non back edge input for reaching
// a fixed point faster.
HInstruction* first_input = phi->InputAt(0);
@@ -742,7 +738,7 @@
}
Handle<mirror::Class> handle = parent_rti.GetTypeHandle();
- if (handle->IsObjectArrayClass() && !handle->GetComponentType()->IsErroneous()) {
+ if (handle->IsObjectArrayClass() && IsAdmissible(handle->GetComponentType())) {
ReferenceTypeInfo::TypeHandle component_handle =
handle_cache->NewHandle(handle->GetComponentType());
bool is_exact = component_handle->CannotBeAssignedFromOtherTypes();
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 7a1bb31..08bd35f 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -99,7 +99,7 @@
if (direct_method != 0u) { // Should we use a direct pointer to the method?
// Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
// kDirectAddress would be fine for image methods, we don't support it at the moment.
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now?
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
method_load_data = direct_method;
@@ -109,7 +109,7 @@
} else { // Use dex cache.
DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile());
if (use_pc_relative_instructions) { // Can we use PC-relative access to the dex cache arrays?
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
&graph_->GetDexFile());
@@ -121,7 +121,7 @@
if (direct_code != 0u) { // Should we use a direct pointer to the code?
// Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
// while kCallDirect would be fine for image methods, we don't support it at the moment.
- DCHECK(!Runtime::Current()->UseJit());
+ DCHECK(!Runtime::Current()->UseJitCompilation());
if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now?
code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
direct_code_ptr = direct_code;
@@ -174,7 +174,7 @@
if (compiler_driver_->IsBootImage()) {
// Compiling boot image. Resolve the string and allocate it if needed.
- DCHECK(!runtime->UseJit());
+ DCHECK(!runtime->UseJitCompilation());
mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
CHECK(string != nullptr);
if (!compiler_driver_->GetSupportBootImageFixup()) {
@@ -187,7 +187,7 @@
? HLoadString::LoadKind::kBootImageLinkTimePcRelative
: HLoadString::LoadKind::kBootImageLinkTimeAddress;
}
- } else if (runtime->UseJit()) {
+ } else if (runtime->UseJitCompilation()) {
// TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
// DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
mirror::String* string = dex_cache->GetResolvedString(string_index);
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 40dab74..1141fd1 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -1003,6 +1003,15 @@
void AddBackEdgeUses(const HBasicBlock& block_at_use) {
DCHECK(block_at_use.IsInLoop());
+ if (block_at_use.GetGraph()->HasIrreducibleLoops()) {
+ // Linear order may not be well formed when irreducible loops are present,
+ // i.e. loop blocks may not be adjacent and a back edge may not be last,
+ // which violates assumptions made in this method.
+ return;
+ }
+
+ DCHECK(IsLinearOrderWellFormed(*block_at_use.GetGraph()));
+
// Add synthesized uses at the back edge of loops to help the register allocator.
// Note that this method is called in decreasing liveness order, to faciliate adding
// uses at the head of the `first_use_` linked list. Because below
@@ -1027,30 +1036,12 @@
if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) {
// There was a use already seen in this loop. Therefore the previous call to `AddUse`
// already inserted the backedge use. We can stop going outward.
- if (kIsDebugBuild) {
- if (!HasSynthesizeUseAt(back_edge_use_position)) {
- // There exists a use prior to `back_edge_use_position` but there is
- // no synthesized use at the back edge. This can happen in the presence
- // of irreducible loops, when blocks of the loop are not adjacent in
- // linear order, i.e. when there is an out-of-loop block between
- // `block_at_use` and `back_edge_position` that uses this interval.
- DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops());
- DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph()));
- }
- }
+ DCHECK(HasSynthesizeUseAt(back_edge_use_position));
break;
}
- if (last_in_new_list != nullptr &&
- back_edge_use_position <= last_in_new_list->GetPosition()) {
- // Loops are not properly nested in the linear order, i.e. the back edge
- // of an outer loop preceeds blocks of an inner loop. This can happen
- // in the presence of irreducible loops.
- DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops());
- DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph()));
- // We must bail out, otherwise we would generate an unsorted use list.
- break;
- }
+ DCHECK(last_in_new_list == nullptr ||
+ back_edge_use_position > last_in_new_list->GetPosition());
UsePosition* new_use = new (allocator_) UsePosition(
/* user */ nullptr,
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index aeb3109..0978ae1 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -139,8 +139,9 @@
continue;
}
- if (phi->InputCount() == 0) {
- DCHECK(phi->IsDead());
+ // If the phi is dead, we know we won't revive it and it will be removed,
+ // so don't process it.
+ if (phi->IsDead()) {
continue;
}
diff --git a/compiler/utils/arena_allocator_test.cc b/compiler/utils/arena_allocator_test.cc
deleted file mode 100644
index 7f67ef1..0000000
--- a/compiler/utils/arena_allocator_test.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "base/arena_allocator.h"
-#include "base/arena_bit_vector.h"
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(ArenaAllocator, Test) {
- ArenaPool pool;
- ArenaAllocator arena(&pool);
- ArenaBitVector bv(&arena, 10, true);
- bv.SetBit(5);
- EXPECT_EQ(1U, bv.GetStorageSize());
- bv.SetBit(35);
- EXPECT_EQ(2U, bv.GetStorageSize());
-}
-
-} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 2c73fb8..546dd65 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <type_traits>
+
#include "assembler_thumb2.h"
#include "base/bit_utils.h"
@@ -25,6 +27,30 @@
namespace art {
namespace arm {
+template <typename Function>
+void Thumb2Assembler::Fixup::ForExpandableDependencies(Thumb2Assembler* assembler, Function fn) {
+ static_assert(
+ std::is_same<typename std::result_of<Function(FixupId, FixupId)>::type, void>::value,
+ "Incorrect signature for argument `fn`: expected (FixupId, FixupId) -> void");
+ Fixup* fixups = assembler->fixups_.data();
+ for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) {
+ uint32_t target = fixups[fixup_id].target_;
+ if (target > fixups[fixup_id].location_) {
+ for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
+ if (fixups[id].CanExpand()) {
+ fn(id, fixup_id);
+ }
+ }
+ } else {
+ for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
+ if (fixups[id - 1u].CanExpand()) {
+ fn(id - 1u, fixup_id);
+ }
+ }
+ }
+ }
+}
+
void Thumb2Assembler::Fixup::PrepareDependents(Thumb2Assembler* assembler) {
// For each Fixup, it's easy to find the Fixups that it depends on as they are either
// the following or the preceding Fixups until we find the target. However, for fixup
@@ -34,24 +60,16 @@
// index and count. (Instead of having a per-fixup vector.)
// Count the number of dependents of each Fixup.
- const FixupId end_id = assembler->fixups_.size();
Fixup* fixups = assembler->fixups_.data();
- for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
- uint32_t target = fixups[fixup_id].target_;
- if (target > fixups[fixup_id].location_) {
- for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
- fixups[id].dependents_count_ += 1u;
- }
- } else {
- for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
- fixups[id - 1u].dependents_count_ += 1u;
- }
- }
- }
+ ForExpandableDependencies(
+ assembler,
+ [fixups](FixupId dependency, FixupId dependent ATTRIBUTE_UNUSED) {
+ fixups[dependency].dependents_count_ += 1u;
+ });
// Assign index ranges in fixup_dependents_ to individual fixups. Record the end of the
// range in dependents_start_, we shall later decrement it as we fill in fixup_dependents_.
uint32_t number_of_dependents = 0u;
- for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
+ for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) {
number_of_dependents += fixups[fixup_id].dependents_count_;
fixups[fixup_id].dependents_start_ = number_of_dependents;
}
@@ -61,20 +79,12 @@
// Create and fill in the fixup_dependents_.
assembler->fixup_dependents_.resize(number_of_dependents);
FixupId* dependents = assembler->fixup_dependents_.data();
- for (FixupId fixup_id = 0u; fixup_id != end_id; ++fixup_id) {
- uint32_t target = fixups[fixup_id].target_;
- if (target > fixups[fixup_id].location_) {
- for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) {
- fixups[id].dependents_start_ -= 1u;
- dependents[fixups[id].dependents_start_] = fixup_id;
- }
- } else {
- for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) {
- fixups[id - 1u].dependents_start_ -= 1u;
- dependents[fixups[id - 1u].dependents_start_] = fixup_id;
- }
- }
- }
+ ForExpandableDependencies(
+ assembler,
+ [fixups, dependents](FixupId dependency, FixupId dependent) {
+ fixups[dependency].dependents_start_ -= 1u;
+ dependents[fixups[dependency].dependents_start_] = dependent;
+ });
}
void Thumb2Assembler::BindLabel(Label* label, uint32_t bound_pc) {
@@ -115,6 +125,7 @@
std::deque<FixupId>* fixups_to_recalculate) {
uint32_t adjustment = fixup->AdjustSizeIfNeeded(*current_code_size);
if (adjustment != 0u) {
+ DCHECK(fixup->CanExpand());
*current_code_size += adjustment;
for (FixupId dependent_id : fixup->Dependents(*this)) {
Fixup* dependent = GetFixup(dependent_id);
@@ -2546,9 +2557,19 @@
}
} else {
branch_type = Fixup::kUnconditional; // B.
+ // The T2 encoding offset is `SignExtend(imm11:'0', 32)` and there is a PC adjustment of 4.
+ static constexpr size_t kMaxT2BackwardDistance = (1u << 11) - 4u;
+ if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT2BackwardDistance) {
+ use32bit = true;
+ }
}
} else {
branch_type = Fixup::kConditional; // B<cond>.
+ // The T1 encoding offset is `SignExtend(imm8:'0', 32)` and there is a PC adjustment of 4.
+ static constexpr size_t kMaxT1BackwardDistance = (1u << 8) - 4u;
+ if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT1BackwardDistance) {
+ use32bit = true;
+ }
}
Fixup::Size size = use32bit ? Fixup::kBranch32Bit : Fixup::kBranch16Bit;
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 111a6b0..ce310a4 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -538,6 +538,20 @@
return GetType() >= kLoadLiteralNarrow;
}
+ // Returns whether the Fixup can expand from the original size.
+ bool CanExpand() const {
+ switch (GetOriginalSize()) {
+ case kBranch32Bit:
+ case kCbxz48Bit:
+ case kLiteralFar:
+ case kLiteralAddrFar:
+ case kLongOrFPLiteralFar:
+ return false;
+ default:
+ return true;
+ }
+ }
+
Size GetOriginalSize() const {
return original_size_;
}
@@ -611,6 +625,7 @@
dependents_count_(0u),
dependents_start_(0u) {
}
+
static size_t SizeInBytes(Size size);
// The size of padding added before the literal pool.
@@ -623,6 +638,9 @@
int32_t LoadWideOrFpEncoding(Register rbase, int32_t offset) const;
+ template <typename Function>
+ static void ForExpandableDependencies(Thumb2Assembler* assembler, Function fn);
+
static constexpr uint32_t kUnresolved = 0xffffffff; // Value for target_ for unresolved.
const Register rn_; // Rn for cbnz/cbz, Rt for literal loads.
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 650b089..b5cafcb 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -372,6 +372,31 @@
DriverStr(expected, "StoreWordPairToNonThumbOffset");
}
+TEST_F(AssemblerThumb2Test, DistantBackBranch) {
+ Label start, end;
+ __ Bind(&start);
+ constexpr size_t kLdrR0R0Count1 = 256;
+ for (size_t i = 0; i != kLdrR0R0Count1; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ b(&end, arm::EQ);
+ __ b(&start, arm::LT);
+ constexpr size_t kLdrR0R0Count2 = 256;
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ Bind(&end);
+
+ std::string expected =
+ "0:\n" +
+ RepeatInsn(kLdrR0R0Count1, "ldr r0, [r0]\n") +
+ "beq 1f\n"
+ "blt 0b\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ "1:\n";
+ DriverStr(expected, "DistantBackBranch");
+}
+
TEST_F(AssemblerThumb2Test, TwoCbzMaxOffset) {
Label label0, label1, label2;
__ cbz(arm::R0, &label1);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 370583e..be38336 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1526,6 +1526,7 @@
instruction_set_,
instruction_set_features_.get(),
IsBootImage(),
+ IsAppImage(),
image_classes_.release(),
compiled_classes_.release(),
/* compiled_methods */ nullptr,
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 3c6a05d..d2ab699 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -59,6 +59,7 @@
#include "stack_map.h"
#include "ScopedLocalRef.h"
#include "thread_list.h"
+#include "type_lookup_table.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
@@ -573,8 +574,15 @@
os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
- // Create the verifier early.
+ // Print embedded dex file data range.
+ const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin();
+ const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer();
+ uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - oat_file_begin);
+ os << StringPrintf("dex-file: 0x%08x..0x%08x\n",
+ dex_offset,
+ dchecked_integral_cast<uint32_t>(dex_offset + oat_dex_file.FileSize() - 1));
+ // Create the dex file early. A lot of print-out things depend on it.
std::string error_msg;
const DexFile* const dex_file = OpenDexFile(&oat_dex_file, &error_msg);
if (dex_file == nullptr) {
@@ -583,6 +591,16 @@
return false;
}
+ // Print lookup table, if it exists.
+ if (oat_dex_file.GetLookupTableData() != nullptr) {
+ uint32_t table_offset = dchecked_integral_cast<uint32_t>(
+ oat_dex_file.GetLookupTableData() - oat_file_begin);
+ uint32_t table_size = TypeLookupTable::RawDataLength(*dex_file);
+ os << StringPrintf("type-table: 0x%08x..0x%08x\n",
+ table_offset,
+ table_offset + table_size - 1);
+ }
+
VariableIndentationOutputStream vios(&os);
ScopedIndentation indent1(&vios);
for (size_t class_def_index = 0;
@@ -1416,11 +1434,10 @@
indent_os << "\n";
// TODO: Dump fields.
// Dump methods after.
- const auto& methods_section = image_header_.GetMethodsSection();
DumpArtMethodVisitor visitor(this);
- methods_section.VisitPackedArtMethods(&visitor,
- image_space_.Begin(),
- image_header_.GetPointerSize());
+ image_header_.VisitPackedArtMethods(&visitor,
+ image_space_.Begin(),
+ image_header_.GetPointerSize());
// Dump the large objects separately.
heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
indent_os << "\n";
@@ -1779,6 +1796,7 @@
DCHECK(method != nullptr);
const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
+ const size_t pointer_size = image_header_.GetPointerSize();
OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
if (method->IsNative()) {
@@ -1792,13 +1810,16 @@
image_header_.GetPointerSize())) {
indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
}
- } else if (method->IsAbstract() ||
- method->IsCalleeSaveMethod() ||
- method->IsResolutionMethod() ||
- (method == Runtime::Current()->GetImtConflictMethod()) ||
- method->IsImtUnimplementedMethod() ||
- method->IsClassInitializer()) {
+ } else if (method->IsAbstract() || method->IsClassInitializer()) {
// Don't print information for these.
+ } else if (method->IsRuntimeMethod()) {
+ ImtConflictTable* table = method->GetImtConflictTable(image_header_.GetPointerSize());
+ if (table != nullptr) {
+ indent_os << "IMT conflict table " << table << " method: ";
+ for (size_t i = 0, count = table->NumEntries(pointer_size); i < count; ++i) {
+ indent_os << PrettyMethod(table->GetImplementationMethod(i, pointer_size)) << " ";
+ }
+ }
} else {
const DexFile::CodeItem* code_item = method->GetCodeItem();
size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 93e40af..0a7ffda 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -472,8 +472,7 @@
void PatchOat::PatchArtFields(const ImageHeader* image_header) {
PatchOatArtFieldVisitor visitor(this);
- const auto& section = image_header->GetImageSection(ImageHeader::kSectionArtFields);
- section.VisitPackedArtFields(&visitor, heap_->Begin());
+ image_header->VisitPackedArtFields(&visitor, heap_->Begin());
}
class PatchOatArtMethodVisitor : public ArtMethodVisitor {
@@ -490,10 +489,20 @@
};
void PatchOat::PatchArtMethods(const ImageHeader* image_header) {
- const auto& section = image_header->GetMethodsSection();
const size_t pointer_size = InstructionSetPointerSize(isa_);
PatchOatArtMethodVisitor visitor(this);
- section.VisitPackedArtMethods(&visitor, heap_->Begin(), pointer_size);
+ image_header->VisitPackedArtMethods(&visitor, heap_->Begin(), pointer_size);
+}
+
+void PatchOat::PatchImtConflictTables(const ImageHeader* image_header) {
+ const size_t pointer_size = InstructionSetPointerSize(isa_);
+ // We can safely walk target image since the conflict tables are independent.
+ image_header->VisitPackedImtConflictTables(
+ [this](ArtMethod* method) {
+ return RelocatedAddressOfPointer(method);
+ },
+ image_->Begin(),
+ pointer_size);
}
class FixupRootVisitor : public RootVisitor {
@@ -627,6 +636,7 @@
PatchArtFields(image_header);
PatchArtMethods(image_header);
+ PatchImtConflictTables(image_header);
PatchInternedStrings(image_header);
PatchClassTable(image_header);
// Patch dex file int/long arrays which point to ArtFields.
@@ -725,6 +735,7 @@
RelocatedAddressOfPointer(object->GetDexCacheResolvedTypes(pointer_size)), pointer_size);
copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
+ // No special handling for IMT conflict table since all pointers are moved by the same offset.
copy->SetEntryPointFromJniPtrSize(RelocatedAddressOfPointer(
object->GetEntryPointFromJniPtrSize(pointer_size)), pointer_size);
}
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index 510ff1e..3ef837f 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -117,6 +117,8 @@
bool PatchImage(bool primary_image) SHARED_REQUIRES(Locks::mutator_lock_);
void PatchArtFields(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_);
void PatchArtMethods(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_);
+ void PatchImtConflictTables(const ImageHeader* image_header)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void PatchInternedStrings(const ImageHeader* image_header)
SHARED_REQUIRES(Locks::mutator_lock_);
void PatchClassTable(const ImageHeader* image_header)
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index 58e8a3a..a25460e 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -21,44 +21,41 @@
namespace art {
-// Minimum number of new methods that profiles must contain to enable recompilation.
+// Minimum number of new methods/classes that profiles
+// must contain to enable recompilation.
static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
+static constexpr const uint32_t kMinNewClassesForCompilation = 10;
ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
const ScopedFlock& reference_profile_file) {
DCHECK(!profile_files.empty());
- std::vector<ProfileCompilationInfo> new_info(profile_files.size());
- bool should_compile = false;
- // Read the main profile files.
- for (size_t i = 0; i < new_info.size(); i++) {
- if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not load profile file at index " << i;
- return kErrorBadProfiles;
- }
- // Do we have enough new profiled methods that will make the compilation worthwhile?
- should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation);
- }
-
- if (!should_compile) {
- return kSkipCompilation;
- }
-
- // Merge information.
ProfileCompilationInfo info;
+ // Load the reference profile.
if (!info.Load(reference_profile_file.GetFile()->Fd())) {
LOG(WARNING) << "Could not load reference profile file";
return kErrorBadProfiles;
}
- for (size_t i = 0; i < new_info.size(); i++) {
- // Merge all data into a single object.
- if (!info.Load(new_info[i])) {
- LOG(WARNING) << "Could not merge profile data at index " << i;
+ // Store the current state of the reference profile before merging with the current profiles.
+ uint32_t number_of_methods = info.GetNumberOfMethods();
+ uint32_t number_of_classes = info.GetNumberOfResolvedClasses();
+
+ // Merge all current profiles.
+ for (size_t i = 0; i < profile_files.size(); i++) {
+ if (!info.Load(profile_files[i].GetFile()->Fd())) {
+ LOG(WARNING) << "Could not load profile file at index " << i;
return kErrorBadProfiles;
}
}
+
+ // Check if there is enough new information added by the current profiles.
+ if (((info.GetNumberOfMethods() - number_of_methods) < kMinNewMethodsForCompilation) &&
+ ((info.GetNumberOfResolvedClasses() - number_of_classes) < kMinNewClassesForCompilation)) {
+ return kSkipCompilation;
+ }
+
// We were successful in merging all profile information. Update the reference profile.
if (!reference_profile_file.GetFile()->ClearContent()) {
PLOG(WARNING) << "Could not clear reference profile file";
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index b0d5df2..462c397 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -29,6 +29,7 @@
void SetupProfile(const std::string& id,
uint32_t checksum,
uint16_t number_of_methods,
+ uint16_t number_of_classes,
const ScratchFile& profile,
ProfileCompilationInfo* info,
uint16_t start_method_index = 0) {
@@ -40,6 +41,10 @@
ASSERT_TRUE(info->AddMethodIndex(dex_location1, dex_location_checksum1, i));
ASSERT_TRUE(info->AddMethodIndex(dex_location2, dex_location_checksum2, i));
}
+ for (uint16_t i = 0; i < number_of_classes; i++) {
+ ASSERT_TRUE(info->AddClassIndex(dex_location1, dex_location_checksum1, i));
+ }
+
ASSERT_TRUE(info->Save(GetFd(profile)));
ASSERT_EQ(0, profile.GetFile()->Flush());
ASSERT_TRUE(profile.GetFile()->ResetOffset());
@@ -89,9 +94,9 @@
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+ SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should advise compilation.
ASSERT_EQ(ProfileAssistant::kCompile,
@@ -102,8 +107,8 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
@@ -111,6 +116,35 @@
CheckProfileInfo(profile2, info2);
}
+// TODO(calin): Add more tests for classes.
+TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
+ ScratchFile profile1;
+ ScratchFile reference_profile;
+
+ std::vector<int> profile_fds({
+ GetFd(profile1)});
+ int reference_profile_fd = GetFd(reference_profile);
+
+ const uint16_t kNumberOfClassesToEnableCompilation = 100;
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
+
+ // We should advise compilation.
+ ASSERT_EQ(ProfileAssistant::kCompile,
+ ProcessProfiles(profile_fds, reference_profile_fd));
+ // The resulting compilation info must be equal to the merge of the inputs.
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(result.Load(reference_profile_fd));
+
+ ProfileCompilationInfo expected;
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.Equals(result));
+
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+}
+
TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
ScratchFile profile1;
ScratchFile profile2;
@@ -124,15 +158,15 @@
// The new profile info will contain the methods with indices 0-100.
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+ SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// The reference profile info will contain the methods with indices 50-150.
const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
ProfileCompilationInfo reference_info;
- SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile,
+ SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
&reference_info, kNumberOfMethodsToEnableCompilation / 2);
// We should advise compilation.
@@ -145,9 +179,9 @@
ASSERT_TRUE(result.Load(reference_profile_fd));
ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Load(reference_info));
+ ASSERT_TRUE(expected.MergeWith(info1));
+ ASSERT_TRUE(expected.MergeWith(info2));
+ ASSERT_TRUE(expected.MergeWith(reference_info));
ASSERT_TRUE(expected.Equals(result));
// The information from profiles must remain the same.
@@ -167,9 +201,9 @@
const uint16_t kNumberOfMethodsToSkipCompilation = 1;
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2);
+ SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
// We should not advise compilation.
ASSERT_EQ(ProfileAssistant::kSkipCompilation,
@@ -207,9 +241,9 @@
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
// Assign different hashes for the same dex file. This will make merging of information to fail.
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
- SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
+ SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should fail processing.
ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
@@ -234,9 +268,9 @@
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
// Assign different hashes for the same dex file. This will make merging of information to fail.
ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
+ SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
ProfileCompilationInfo reference_info;
- SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info);
+ SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, reference_profile, &reference_info);
// We should not advise compilation.
ASSERT_TRUE(profile1.GetFile()->ResetOffset());
diff --git a/profman/profman.cc b/profman/profman.cc
index 7c9e449..3e632bc 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -14,12 +14,14 @@
* limitations under the License.
*/
+#include "errno.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <iostream>
#include <string>
#include <vector>
@@ -68,6 +70,9 @@
UsageError("Command: %s", CommandLine().c_str());
UsageError("Usage: profman [options]...");
UsageError("");
+ UsageError(" --dump-info-for=<filename>: dumps the content of the profile file");
+ UsageError(" to standard output in a human readable form.");
+ UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
UsageError(" profiles will be aggregated.");
@@ -117,9 +122,11 @@
const StringPiece option(argv[i]);
const bool log_options = false;
if (log_options) {
- LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i];
+ LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
}
- if (option.starts_with("--profile-file=")) {
+ if (option.starts_with("--dump-info-for=")) {
+ dump_info_for_ = option.substr(strlen("--dump-info-for=")).ToString();
+ } else if (option.starts_with("--profile-file=")) {
profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
} else if (option.starts_with("--profile-file-fd=")) {
ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
@@ -132,13 +139,23 @@
}
}
- if (profile_files_.empty() && profile_files_fd_.empty()) {
+ bool has_profiles = !profile_files_.empty() || !profile_files_fd_.empty();
+ bool has_reference_profile = !reference_profile_file_.empty() ||
+ (reference_profile_file_fd_ != -1);
+
+ if (!dump_info_for_.empty()) {
+ if (has_profiles || has_reference_profile) {
+ Usage("dump-info-for cannot be specified together with other options");
+ }
+ return;
+ }
+ if (!has_profiles) {
Usage("No profile files specified.");
}
if (!profile_files_.empty() && !profile_files_fd_.empty()) {
Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
}
- if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) {
+ if (!has_reference_profile) {
Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd");
}
if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) {
@@ -160,6 +177,27 @@
return result;
}
+ int DumpProfileInfo() {
+ int fd = open(dump_info_for_.c_str(), O_RDWR);
+ if (fd < 0) {
+ std::cerr << "Cannot open " << dump_info_for_ << strerror(errno);
+ return -1;
+ }
+ ProfileCompilationInfo info;
+ if (!info.Load(fd)) {
+ std::cerr << "Cannot load profile info from " << dump_info_for_;
+ return -1;
+ }
+ std::string dump = info.DumpInfo(/*dex_files*/ nullptr);
+ info.Save(fd);
+ std::cout << dump << "\n";
+ return 0;
+ }
+
+ bool ShouldOnlyDumpProfile() {
+ return !dump_info_for_.empty();
+ }
+
private:
static void ParseFdForCollection(const StringPiece& option,
const char* arg_name,
@@ -186,6 +224,7 @@
std::string reference_profile_file_;
int reference_profile_file_fd_;
uint64_t start_ns_;
+ std::string dump_info_for_;
};
// See ProfileAssistant::ProcessingResult for return codes.
@@ -195,6 +234,9 @@
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
profman.ParseArgs(argc, argv);
+ if (profman.ShouldOnlyDumpProfile()) {
+ return profman.DumpProfileInfo();
+ }
// Process profile information and assess if we need to do a profile guided compilation.
// This operation involves I/O.
return profman.ProcessProfiles();
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index e358ff8..f0e9ac5 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -17,6 +17,7 @@
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/math_entrypoints.h"
@@ -47,67 +48,12 @@
extern "C" int64_t __aeabi_ldivmod(int64_t, int64_t);
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
- // JNI
- jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
- // Alloc
- ResetQuickAllocEntryPoints(qpoints);
+ DefaultInitEntryPoints(jpoints, qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
qpoints->pCheckCast = art_quick_check_cast;
- // DexCache
- qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
- qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
- qpoints->pInitializeType = art_quick_initialize_type;
- qpoints->pResolveString = art_quick_resolve_string;
-
- // Field
- qpoints->pSet8Instance = art_quick_set8_instance;
- qpoints->pSet8Static = art_quick_set8_static;
- qpoints->pSet16Instance = art_quick_set16_instance;
- qpoints->pSet16Static = art_quick_set16_static;
- qpoints->pSet32Instance = art_quick_set32_instance;
- qpoints->pSet32Static = art_quick_set32_static;
- qpoints->pSet64Instance = art_quick_set64_instance;
- qpoints->pSet64Static = art_quick_set64_static;
- qpoints->pSetObjInstance = art_quick_set_obj_instance;
- qpoints->pSetObjStatic = art_quick_set_obj_static;
- qpoints->pGetByteInstance = art_quick_get_byte_instance;
- qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
- qpoints->pGetShortInstance = art_quick_get_short_instance;
- qpoints->pGetCharInstance = art_quick_get_char_instance;
- qpoints->pGet32Instance = art_quick_get32_instance;
- qpoints->pGet64Instance = art_quick_get64_instance;
- qpoints->pGetObjInstance = art_quick_get_obj_instance;
- qpoints->pGetByteStatic = art_quick_get_byte_static;
- qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
- qpoints->pGetShortStatic = art_quick_get_short_static;
- qpoints->pGetCharStatic = art_quick_get_char_static;
- qpoints->pGet32Static = art_quick_get32_static;
- qpoints->pGet64Static = art_quick_get64_static;
- qpoints->pGetObjStatic = art_quick_get_obj_static;
-
- // Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
- // JNI
- qpoints->pJniMethodStart = JniMethodStart;
- qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
- qpoints->pJniMethodEnd = JniMethodEnd;
- qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
- qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
- qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
- qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
- // Locks
- qpoints->pLockObject = art_quick_lock_object;
- qpoints->pUnlockObject = art_quick_unlock_object;
-
// Math
qpoints->pIdivmod = __aeabi_idivmod;
qpoints->pLdiv = __aeabi_ldivmod;
@@ -154,35 +100,6 @@
qpoints->pStringCompareTo = art_quick_string_compareto;
qpoints->pMemcpy = memcpy;
- // Invocation
- qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
- qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
- qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
- qpoints->pInvokeDirectTrampolineWithAccessCheck =
- art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
- art_quick_invoke_interface_trampoline_with_access_check;
- qpoints->pInvokeStaticTrampolineWithAccessCheck =
- art_quick_invoke_static_trampoline_with_access_check;
- qpoints->pInvokeSuperTrampolineWithAccessCheck =
- art_quick_invoke_super_trampoline_with_access_check;
- qpoints->pInvokeVirtualTrampolineWithAccessCheck =
- art_quick_invoke_virtual_trampoline_with_access_check;
-
- // Thread
- qpoints->pTestSuspend = art_quick_test_suspend;
-
- // Throws
- qpoints->pDeliverException = art_quick_deliver_exception;
- qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
- qpoints->pThrowDivZero = art_quick_throw_div_zero;
- qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
- qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
- qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
- // Deoptimization from compiled code.
- qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
qpoints->pReadBarrierMark = artReadBarrierMark;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index e6ff0aa..321b9d2 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -544,6 +544,15 @@
DELIVER_PENDING_EXCEPTION
END art_quick_lock_object
+ENTRY art_quick_lock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case we block
+ mov r1, r9 @ pass Thread::Current
+ bl artLockObjectFromCode @ (Object* obj, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_lock_object_no_inline
+
/*
* Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
* r0 holds the possibly null object to lock.
@@ -601,6 +610,16 @@
DELIVER_PENDING_EXCEPTION
END art_quick_unlock_object
+ENTRY art_quick_unlock_object_no_inline
+ @ save callee saves in case exception allocation triggers GC
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2
+ mov r1, r9 @ pass Thread::Current
+ bl artUnlockObjectFromCode @ (Object* obj, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_unlock_object_no_inline
+
/*
* Entry from managed code that calls artIsAssignableFromCode and on failure calls
* artThrowClassCastException.
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 4db9411..bf0f647 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -17,6 +17,7 @@
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/math_entrypoints.h"
@@ -30,67 +31,12 @@
const mirror::Class* ref_class);
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
- // JNI
- jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
- // Alloc
- ResetQuickAllocEntryPoints(qpoints);
+ DefaultInitEntryPoints(jpoints, qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
qpoints->pCheckCast = art_quick_check_cast;
- // DexCache
- qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
- qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
- qpoints->pInitializeType = art_quick_initialize_type;
- qpoints->pResolveString = art_quick_resolve_string;
-
- // Field
- qpoints->pSet8Instance = art_quick_set8_instance;
- qpoints->pSet8Static = art_quick_set8_static;
- qpoints->pSet16Instance = art_quick_set16_instance;
- qpoints->pSet16Static = art_quick_set16_static;
- qpoints->pSet32Instance = art_quick_set32_instance;
- qpoints->pSet32Static = art_quick_set32_static;
- qpoints->pSet64Instance = art_quick_set64_instance;
- qpoints->pSet64Static = art_quick_set64_static;
- qpoints->pSetObjInstance = art_quick_set_obj_instance;
- qpoints->pSetObjStatic = art_quick_set_obj_static;
- qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
- qpoints->pGetByteInstance = art_quick_get_byte_instance;
- qpoints->pGetCharInstance = art_quick_get_char_instance;
- qpoints->pGetShortInstance = art_quick_get_short_instance;
- qpoints->pGet32Instance = art_quick_get32_instance;
- qpoints->pGet64Instance = art_quick_get64_instance;
- qpoints->pGetObjInstance = art_quick_get_obj_instance;
- qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
- qpoints->pGetByteStatic = art_quick_get_byte_static;
- qpoints->pGetCharStatic = art_quick_get_char_static;
- qpoints->pGetShortStatic = art_quick_get_short_static;
- qpoints->pGet32Static = art_quick_get32_static;
- qpoints->pGet64Static = art_quick_get64_static;
- qpoints->pGetObjStatic = art_quick_get_obj_static;
-
- // Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
- // JNI
- qpoints->pJniMethodStart = JniMethodStart;
- qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
- qpoints->pJniMethodEnd = JniMethodEnd;
- qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
- qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
- qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
- qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
- // Locks
- qpoints->pLockObject = art_quick_lock_object;
- qpoints->pUnlockObject = art_quick_unlock_object;
-
// Math
// TODO null entrypoints not needed for ARM64 - generate inline.
qpoints->pCmpgDouble = nullptr;
@@ -134,38 +80,10 @@
// Intrinsics
qpoints->pIndexOf = art_quick_indexof;
- qpoints->pStringCompareTo = art_quick_string_compareto;
+ // The ARM64 StringCompareTo intrinsic does not call the runtime.
+ qpoints->pStringCompareTo = nullptr;
qpoints->pMemcpy = memcpy;
- // Invocation
- qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
- qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
- qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
- qpoints->pInvokeDirectTrampolineWithAccessCheck =
- art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
- art_quick_invoke_interface_trampoline_with_access_check;
- qpoints->pInvokeStaticTrampolineWithAccessCheck =
- art_quick_invoke_static_trampoline_with_access_check;
- qpoints->pInvokeSuperTrampolineWithAccessCheck =
- art_quick_invoke_super_trampoline_with_access_check;
- qpoints->pInvokeVirtualTrampolineWithAccessCheck =
- art_quick_invoke_virtual_trampoline_with_access_check;
-
- // Thread
- qpoints->pTestSuspend = art_quick_test_suspend;
-
- // Throws
- qpoints->pDeliverException = art_quick_deliver_exception;
- qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
- qpoints->pThrowDivZero = art_quick_throw_div_zero;
- qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
- qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
- qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
- // Deoptimization from compiled code.
- qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
qpoints->pReadBarrierMark = artReadBarrierMark;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index 613bb5c..cad13b2 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -39,7 +39,7 @@
if (!needs_a53_835769_fix) {
// Check to see if this is an expected variant.
static const char* arm64_known_variants[] = {
- "denver64", "kryo"
+ "denver64", "kryo", "exynos-m1"
};
if (!FindVariantInArray(arm64_known_variants, arraysize(arm64_known_variants), variant)) {
std::ostringstream os;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 1cdda2d..1fba09b 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1113,6 +1113,14 @@
RETURN_IF_W0_IS_ZERO_OR_DELIVER
END art_quick_lock_object
+ENTRY art_quick_lock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case we block
+ mov x1, xSELF // pass Thread::Current
+ bl artLockObjectFromCode // (Object* obj, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_W0_IS_ZERO_OR_DELIVER
+END art_quick_lock_object_no_inline
+
/*
* Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
* x0 holds the possibly null object to lock.
@@ -1171,6 +1179,14 @@
RETURN_IF_W0_IS_ZERO_OR_DELIVER
END art_quick_unlock_object
+ENTRY art_quick_unlock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case exception allocation triggers GC
+ mov x1, xSELF // pass Thread::Current
+ bl artUnlockObjectFromCode // (Object* obj, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_W0_IS_ZERO_OR_DELIVER
+END art_quick_unlock_object_no_inline
+
/*
* Entry from managed code that calls artIsAssignableFromCode and on failure calls
* artThrowClassCastException.
@@ -2187,108 +2203,3 @@
asr x0, x0, #1
ret
END art_quick_indexof
-
- /*
- * String's compareTo.
- *
- * TODO: Not very optimized.
- *
- * On entry:
- * x0: this object pointer
- * x1: comp object pointer
- *
- */
- .extern __memcmp16
-ENTRY art_quick_string_compareto
- mov x2, x0 // x0 is return, use x2 for first input.
- sub x0, x2, x1 // Same string object?
- cbnz x0,1f
- ret
-1: // Different string objects.
-
- ldr w4, [x2, #MIRROR_STRING_COUNT_OFFSET]
- ldr w3, [x1, #MIRROR_STRING_COUNT_OFFSET]
- add x2, x2, #MIRROR_STRING_VALUE_OFFSET
- add x1, x1, #MIRROR_STRING_VALUE_OFFSET
-
- /*
- * Now: Data* Count
- * first arg x2 w4
- * second arg x1 w3
- */
-
- // x0 := str1.length(w4) - str2.length(w3). ldr zero-extended w3/w4 into x3/x4.
- subs x0, x4, x3
- // Min(count1, count2) into w3.
- csel x3, x3, x4, ge
-
- // TODO: Tune this value.
- // Check for long string, do memcmp16 for them.
- cmp w3, #28 // Constant from arm32.
- bgt .Ldo_memcmp16
-
- /*
- * Now:
- * x2: *first string data
- * x1: *second string data
- * w3: iteration count
- * x0: return value if comparison equal
- * x4, x5, x6, x7: free
- */
-
- // Do a simple unrolled loop.
-.Lloop:
- // At least two more elements?
- subs w3, w3, #2
- b.lt .Lremainder_or_done
-
- ldrh w4, [x2], #2
- ldrh w5, [x1], #2
-
- ldrh w6, [x2], #2
- ldrh w7, [x1], #2
-
- subs w4, w4, w5
- b.ne .Lw4_result
-
- subs w6, w6, w7
- b.ne .Lw6_result
-
- b .Lloop
-
-.Lremainder_or_done:
- adds w3, w3, #1
- b.eq .Lremainder
- ret
-
-.Lremainder:
- ldrh w4, [x2], #2
- ldrh w5, [x1], #2
- subs w4, w4, w5
- b.ne .Lw4_result
- ret
-
-// Result is in w4
-.Lw4_result:
- sxtw x0, w4
- ret
-
-// Result is in w6
-.Lw6_result:
- sxtw x0, w6
- ret
-
-.Ldo_memcmp16:
- mov x14, x0 // Save x0 and LR. __memcmp16 does not use these temps.
- mov x15, xLR // TODO: Codify and check that?
-
- mov x0, x2
- uxtw x2, w3
- bl __memcmp16
-
- mov xLR, x15 // Restore LR.
-
- cmp x0, #0 // Check the memcmp difference.
- csel x0, x0, x14, ne // x0 := x0 != 0 ? x14(prev x0=length diff) : x1.
- ret
-END art_quick_string_compareto
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 51eb77f..45e33a8 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -59,6 +59,9 @@
extern "C" int64_t __moddi3(int64_t, int64_t);
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
+ // Note: MIPS has asserts checking for the type of entrypoint. Don't move it
+ // to InitDefaultEntryPoints().
+
// JNI
jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
@@ -167,9 +170,14 @@
"Non-direct C stub marked direct.");
// Locks
- qpoints->pLockObject = art_quick_lock_object;
+ if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
+ qpoints->pLockObject = art_quick_lock_object_no_inline;
+ qpoints->pUnlockObject = art_quick_unlock_object_no_inline;
+ } else {
+ qpoints->pLockObject = art_quick_lock_object;
+ qpoints->pUnlockObject = art_quick_unlock_object;
+ }
static_assert(!IsDirectEntrypoint(kQuickLockObject), "Non-direct C stub marked direct.");
- qpoints->pUnlockObject = art_quick_unlock_object;
static_assert(!IsDirectEntrypoint(kQuickUnlockObject), "Non-direct C stub marked direct.");
// Math
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 8939a48..3ee26af 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -906,6 +906,16 @@
RETURN_IF_ZERO
END art_quick_lock_object
+ENTRY art_quick_lock_object_no_inline
+ beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+ nop
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block
+ la $t9, artLockObjectFromCode
+ jalr $t9 # (Object* obj, Thread*)
+ move $a1, rSELF # pass Thread::Current
+ RETURN_IF_ZERO
+END art_quick_lock_object_no_inline
+
/*
* Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
*/
@@ -920,6 +930,16 @@
RETURN_IF_ZERO
END art_quick_unlock_object
+ENTRY art_quick_unlock_object_no_inline
+ beqz $a0, .Lart_quick_throw_null_pointer_exception_gp_set
+ nop
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
+ la $t9, artUnlockObjectFromCode
+ jalr $t9 # (Object* obj, Thread*)
+ move $a1, rSELF # pass Thread::Current
+ RETURN_IF_ZERO
+END art_quick_unlock_object_no_inline
+
/*
* Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
*/
diff --git a/runtime/arch/mips64/entrypoints_init_mips64.cc b/runtime/arch/mips64/entrypoints_init_mips64.cc
index 4bdb38e..030c127 100644
--- a/runtime/arch/mips64/entrypoints_init_mips64.cc
+++ b/runtime/arch/mips64/entrypoints_init_mips64.cc
@@ -18,6 +18,7 @@
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/math_entrypoints.h"
@@ -57,67 +58,12 @@
extern "C" int64_t __moddi3(int64_t, int64_t);
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
- // JNI
- jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
- // Alloc
- ResetQuickAllocEntryPoints(qpoints);
+ DefaultInitEntryPoints(jpoints, qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
qpoints->pCheckCast = art_quick_check_cast;
- // DexCache
- qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
- qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
- qpoints->pInitializeType = art_quick_initialize_type;
- qpoints->pResolveString = art_quick_resolve_string;
-
- // Field
- qpoints->pSet8Instance = art_quick_set8_instance;
- qpoints->pSet8Static = art_quick_set8_static;
- qpoints->pSet16Instance = art_quick_set16_instance;
- qpoints->pSet16Static = art_quick_set16_static;
- qpoints->pSet32Instance = art_quick_set32_instance;
- qpoints->pSet32Static = art_quick_set32_static;
- qpoints->pSet64Instance = art_quick_set64_instance;
- qpoints->pSet64Static = art_quick_set64_static;
- qpoints->pSetObjInstance = art_quick_set_obj_instance;
- qpoints->pSetObjStatic = art_quick_set_obj_static;
- qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
- qpoints->pGetByteInstance = art_quick_get_byte_instance;
- qpoints->pGetCharInstance = art_quick_get_char_instance;
- qpoints->pGetShortInstance = art_quick_get_short_instance;
- qpoints->pGet32Instance = art_quick_get32_instance;
- qpoints->pGet64Instance = art_quick_get64_instance;
- qpoints->pGetObjInstance = art_quick_get_obj_instance;
- qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
- qpoints->pGetByteStatic = art_quick_get_byte_static;
- qpoints->pGetCharStatic = art_quick_get_char_static;
- qpoints->pGetShortStatic = art_quick_get_short_static;
- qpoints->pGet32Static = art_quick_get32_static;
- qpoints->pGet64Static = art_quick_get64_static;
- qpoints->pGetObjStatic = art_quick_get_obj_static;
-
- // Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
- // JNI
- qpoints->pJniMethodStart = JniMethodStart;
- qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
- qpoints->pJniMethodEnd = JniMethodEnd;
- qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
- qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
- qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
- qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
- // Locks
- qpoints->pLockObject = art_quick_lock_object;
- qpoints->pUnlockObject = art_quick_unlock_object;
-
// Math
qpoints->pCmpgDouble = CmpgDouble;
qpoints->pCmpgFloat = CmpgFloat;
@@ -144,35 +90,6 @@
qpoints->pStringCompareTo = art_quick_string_compareto;
qpoints->pMemcpy = memcpy;
- // Invocation
- qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
- qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
- qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
- qpoints->pInvokeDirectTrampolineWithAccessCheck =
- art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
- art_quick_invoke_interface_trampoline_with_access_check;
- qpoints->pInvokeStaticTrampolineWithAccessCheck =
- art_quick_invoke_static_trampoline_with_access_check;
- qpoints->pInvokeSuperTrampolineWithAccessCheck =
- art_quick_invoke_super_trampoline_with_access_check;
- qpoints->pInvokeVirtualTrampolineWithAccessCheck =
- art_quick_invoke_virtual_trampoline_with_access_check;
-
- // Thread
- qpoints->pTestSuspend = art_quick_test_suspend;
-
- // Throws
- qpoints->pDeliverException = art_quick_deliver_exception;
- qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
- qpoints->pThrowDivZero = art_quick_throw_div_zero;
- qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
- qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
- qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
- // Deoptimization from compiled code.
- qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
// TODO - use lld/scd instructions for Mips64
// Atomic 64-bit load/store
qpoints->pA64Load = QuasiAtomic::Read64;
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 5d0c94c..8f1a35a 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -971,6 +971,15 @@
RETURN_IF_ZERO
END art_quick_lock_object
+ENTRY art_quick_lock_object_no_inline
+ beq $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+ nop
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block
+ jal artLockObjectFromCode # (Object* obj, Thread*)
+ move $a1, rSELF # pass Thread::Current
+ RETURN_IF_ZERO
+END art_quick_lock_object_no_inline
+
/*
* Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
*/
@@ -984,6 +993,15 @@
RETURN_IF_ZERO
END art_quick_unlock_object
+ENTRY art_quick_unlock_object_no_inline
+ beq $a0, $zero, .Lart_quick_throw_null_pointer_exception_gp_set
+ nop
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
+ jal artUnlockObjectFromCode # (Object* obj, Thread*)
+ move $a1, rSELF # pass Thread::Current
+ RETURN_IF_ZERO
+END art_quick_unlock_object_no_inline
+
/*
* Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
*/
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 75d9073..02629e8 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1205,7 +1205,8 @@
TEST_F(StubTest, StringCompareTo) {
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || \
+ // There is no StringCompareTo runtime entrypoint for __aarch64__.
+#if defined(__i386__) || defined(__arm__) || \
defined(__mips__) || (defined(__x86_64__) && !defined(__APPLE__))
// TODO: Check the "Unresolved" allocation stubs
@@ -2010,14 +2011,14 @@
// that will create it: the runtime stub expects to be called by compiled code.
LinearAlloc* linear_alloc = Runtime::Current()->GetLinearAlloc();
ArtMethod* conflict_method = Runtime::Current()->CreateImtConflictMethod(linear_alloc);
- static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
- ImtConflictTable* empty_conflict_table = reinterpret_cast<ImtConflictTable*>(&empty_entry);
+ ImtConflictTable* empty_conflict_table =
+ Runtime::Current()->GetClassLinker()->CreateImtConflictTable(/*count*/0u, linear_alloc);
void* data = linear_alloc->Alloc(
self,
- ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table));
+ ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table, sizeof(void*)));
ImtConflictTable* new_table = new (data) ImtConflictTable(
- empty_conflict_table, inf_contains, contains_amethod);
- conflict_method->SetImtConflictTable(new_table);
+ empty_conflict_table, inf_contains, contains_amethod, sizeof(void*));
+ conflict_method->SetImtConflictTable(new_table, sizeof(void*));
size_t result =
Invoke3WithReferrerAndHidden(reinterpret_cast<size_t>(conflict_method),
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index e593f39..15a8571 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -17,6 +17,7 @@
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "interpreter/interpreter.h"
@@ -33,67 +34,12 @@
extern "C" mirror::Object* art_quick_read_barrier_for_root_slow(GcRoot<mirror::Object>*);
void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
- // JNI
- jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
- // Alloc
- ResetQuickAllocEntryPoints(qpoints);
+ DefaultInitEntryPoints(jpoints, qpoints);
// Cast
qpoints->pInstanceofNonTrivial = art_quick_is_assignable;
qpoints->pCheckCast = art_quick_check_cast;
- // DexCache
- qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
- qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
- qpoints->pInitializeType = art_quick_initialize_type;
- qpoints->pResolveString = art_quick_resolve_string;
-
- // Field
- qpoints->pSet8Instance = art_quick_set8_instance;
- qpoints->pSet8Static = art_quick_set8_static;
- qpoints->pSet16Instance = art_quick_set16_instance;
- qpoints->pSet16Static = art_quick_set16_static;
- qpoints->pSet32Instance = art_quick_set32_instance;
- qpoints->pSet32Static = art_quick_set32_static;
- qpoints->pSet64Instance = art_quick_set64_instance;
- qpoints->pSet64Static = art_quick_set64_static;
- qpoints->pSetObjInstance = art_quick_set_obj_instance;
- qpoints->pSetObjStatic = art_quick_set_obj_static;
- qpoints->pGetByteInstance = art_quick_get_byte_instance;
- qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
- qpoints->pGetShortInstance = art_quick_get_short_instance;
- qpoints->pGetCharInstance = art_quick_get_char_instance;
- qpoints->pGet32Instance = art_quick_get32_instance;
- qpoints->pGet64Instance = art_quick_get64_instance;
- qpoints->pGetObjInstance = art_quick_get_obj_instance;
- qpoints->pGetByteStatic = art_quick_get_byte_static;
- qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
- qpoints->pGetShortStatic = art_quick_get_short_static;
- qpoints->pGetCharStatic = art_quick_get_char_static;
- qpoints->pGet32Static = art_quick_get32_static;
- qpoints->pGet64Static = art_quick_get64_static;
- qpoints->pGetObjStatic = art_quick_get_obj_static;
-
- // Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
- // JNI
- qpoints->pJniMethodStart = JniMethodStart;
- qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
- qpoints->pJniMethodEnd = JniMethodEnd;
- qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
- qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
- qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
- qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
- // Locks
- qpoints->pLockObject = art_quick_lock_object;
- qpoints->pUnlockObject = art_quick_unlock_object;
-
// More math.
qpoints->pCos = cos;
qpoints->pSin = sin;
@@ -128,35 +74,6 @@
qpoints->pStringCompareTo = art_quick_string_compareto;
qpoints->pMemcpy = art_quick_memcpy;
- // Invocation
- qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
- qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
- qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
- qpoints->pInvokeDirectTrampolineWithAccessCheck =
- art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
- art_quick_invoke_interface_trampoline_with_access_check;
- qpoints->pInvokeStaticTrampolineWithAccessCheck =
- art_quick_invoke_static_trampoline_with_access_check;
- qpoints->pInvokeSuperTrampolineWithAccessCheck =
- art_quick_invoke_super_trampoline_with_access_check;
- qpoints->pInvokeVirtualTrampolineWithAccessCheck =
- art_quick_invoke_virtual_trampoline_with_access_check;
-
- // Thread
- qpoints->pTestSuspend = art_quick_test_suspend;
-
- // Throws
- qpoints->pDeliverException = art_quick_deliver_exception;
- qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
- qpoints->pThrowDivZero = art_quick_throw_div_zero;
- qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
- qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
- qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
- // Deoptimize
- qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
qpoints->pReadBarrierMark = art_quick_read_barrier_mark;
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 4f9b3f7..485da9f 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1075,6 +1075,22 @@
RETURN_IF_EAX_ZERO
END_FUNCTION art_quick_lock_object
+DEFINE_FUNCTION art_quick_lock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
+ // Outgoing argument set up
+ subl LITERAL(8), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(8)
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH eax // pass object
+ call SYMBOL(artLockObjectFromCode) // artLockObjectFromCode(object, Thread*)
+ addl LITERAL(16), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-16)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_lock_object_no_inline
+
+
DEFINE_FUNCTION art_quick_unlock_object
testl %eax, %eax // null check object/eax
jz .Lslow_unlock
@@ -1130,6 +1146,21 @@
RETURN_IF_EAX_ZERO
END_FUNCTION art_quick_unlock_object
+DEFINE_FUNCTION art_quick_unlock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
+ // Outgoing argument set up
+ subl LITERAL(8), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(8)
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH eax // pass object
+ call SYMBOL(artUnlockObjectFromCode) // artUnlockObjectFromCode(object, Thread*)
+ addl LITERAL(16), %esp // pop arguments
+ CFI_ADJUST_CFA_OFFSET(-16)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_unlock_object_no_inline
+
DEFINE_FUNCTION art_quick_is_assignable
PUSH eax // alignment padding
PUSH ecx // pass arg2 - obj->klass
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index 0a5d14a..bd6df70 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -17,6 +17,9 @@
#include "entrypoints/jni/jni_entrypoints.h"
#include "entrypoints/quick/quick_alloc_entrypoints.h"
#include "entrypoints/quick/quick_default_externs.h"
+#if !defined(__APPLE__)
+#include "entrypoints/quick/quick_default_init_entrypoints.h"
+#endif
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/math_entrypoints.h"
#include "entrypoints/runtime_asm_entrypoints.h"
@@ -38,67 +41,12 @@
UNUSED(jpoints, qpoints);
UNIMPLEMENTED(FATAL);
#else
- // JNI
- jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
-
- // Alloc
- ResetQuickAllocEntryPoints(qpoints);
+ DefaultInitEntryPoints(jpoints, qpoints);
// Cast
qpoints->pInstanceofNonTrivial = art_quick_assignable_from_code;
qpoints->pCheckCast = art_quick_check_cast;
- // DexCache
- qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
- qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
- qpoints->pInitializeType = art_quick_initialize_type;
- qpoints->pResolveString = art_quick_resolve_string;
-
- // Field
- qpoints->pSet8Instance = art_quick_set8_instance;
- qpoints->pSet8Static = art_quick_set8_static;
- qpoints->pSet16Instance = art_quick_set16_instance;
- qpoints->pSet16Static = art_quick_set16_static;
- qpoints->pSet32Instance = art_quick_set32_instance;
- qpoints->pSet32Static = art_quick_set32_static;
- qpoints->pSet64Instance = art_quick_set64_instance;
- qpoints->pSet64Static = art_quick_set64_static;
- qpoints->pSetObjInstance = art_quick_set_obj_instance;
- qpoints->pSetObjStatic = art_quick_set_obj_static;
- qpoints->pGetByteInstance = art_quick_get_byte_instance;
- qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
- qpoints->pGetShortInstance = art_quick_get_short_instance;
- qpoints->pGetCharInstance = art_quick_get_char_instance;
- qpoints->pGet32Instance = art_quick_get32_instance;
- qpoints->pGet64Instance = art_quick_get64_instance;
- qpoints->pGetObjInstance = art_quick_get_obj_instance;
- qpoints->pGetByteStatic = art_quick_get_byte_static;
- qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
- qpoints->pGetShortStatic = art_quick_get_short_static;
- qpoints->pGetCharStatic = art_quick_get_char_static;
- qpoints->pGet32Static = art_quick_get32_static;
- qpoints->pGet64Static = art_quick_get64_static;
- qpoints->pGetObjStatic = art_quick_get_obj_static;
-
- // Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
-
- // JNI
- qpoints->pJniMethodStart = JniMethodStart;
- qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
- qpoints->pJniMethodEnd = JniMethodEnd;
- qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
- qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
- qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
- qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
-
- // Locks
- qpoints->pLockObject = art_quick_lock_object;
- qpoints->pUnlockObject = art_quick_unlock_object;
-
// More math.
qpoints->pCos = cos;
qpoints->pSin = sin;
@@ -132,35 +80,6 @@
qpoints->pStringCompareTo = art_quick_string_compareto;
qpoints->pMemcpy = art_quick_memcpy;
- // Invocation
- qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
- qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
- qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
- qpoints->pInvokeDirectTrampolineWithAccessCheck =
- art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
- art_quick_invoke_interface_trampoline_with_access_check;
- qpoints->pInvokeStaticTrampolineWithAccessCheck =
- art_quick_invoke_static_trampoline_with_access_check;
- qpoints->pInvokeSuperTrampolineWithAccessCheck =
- art_quick_invoke_super_trampoline_with_access_check;
- qpoints->pInvokeVirtualTrampolineWithAccessCheck =
- art_quick_invoke_virtual_trampoline_with_access_check;
-
- // Thread
- qpoints->pTestSuspend = art_quick_test_suspend;
-
- // Throws
- qpoints->pDeliverException = art_quick_deliver_exception;
- qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
- qpoints->pThrowDivZero = art_quick_throw_div_zero;
- qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
- qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
- qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
-
- // Deoptimize
- qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
-
// Read barrier.
qpoints->pReadBarrierJni = ReadBarrierJni;
qpoints->pReadBarrierMark = art_quick_read_barrier_mark;
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 26e668e..562ee2d 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -994,6 +994,14 @@
RETURN_IF_EAX_ZERO
END_FUNCTION art_quick_lock_object
+DEFINE_FUNCTION art_quick_lock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+ movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
+ call SYMBOL(artLockObjectFromCode) // artLockObjectFromCode(object, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_lock_object_no_inline
+
DEFINE_FUNCTION art_quick_unlock_object
testl %edi, %edi // null check object/edi
jz .Lslow_unlock
@@ -1037,6 +1045,14 @@
RETURN_IF_EAX_ZERO
END_FUNCTION art_quick_unlock_object
+DEFINE_FUNCTION art_quick_unlock_object_no_inline
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
+ movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
+ call SYMBOL(artUnlockObjectFromCode) // artUnlockObjectFromCode(object, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_unlock_object_no_inline
+
DEFINE_FUNCTION art_quick_check_cast
PUSH rdi // Save args for exc
PUSH rsi
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 6449efa..7647ad6 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -456,13 +456,18 @@
interface_method->VisitRoots(visitor, pointer_size);
}
visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
- // Runtime methods and native methods use the same field as the profiling info for
- // storing their own data (jni entrypoint for native methods, and ImtConflictTable for
- // some runtime methods).
- if (!IsNative() && !IsRuntimeMethod()) {
- ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
- if (profiling_info != nullptr) {
- profiling_info->VisitRoots(visitor);
+ // We know we don't have profiling information if the class hasn't been verified. Note
+ // that this check also ensures the IsNative call can be made, as IsNative expects a fully
+ // created class (and not a retired one).
+ if (klass->IsVerified()) {
+ // Runtime methods and native methods use the same field as the profiling info for
+ // storing their own data (jni entrypoint for native methods, and ImtConflictTable for
+ // some runtime methods).
+ if (!IsNative() && !IsRuntimeMethod()) {
+ 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 34d19d1..06156f5 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -276,7 +276,7 @@
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
- CHECK(!runtime->UseJit());
+ CHECK(!runtime->UseJitCompilation());
const void* oat_quick_code = runtime->GetClassLinker()->GetOatMethodQuickCodeFor(this);
CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
<< "Don't call compiled code when -Xint " << PrettyMethod(this);
@@ -481,7 +481,7 @@
// to the JIT code, but this would require taking the JIT code cache lock to notify
// it, which we do not want at this level.
Runtime* runtime = Runtime::Current();
- if (runtime->GetJit() != nullptr) {
+ if (runtime->UseJitCompilation()) {
if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
}
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d1ef019..a012a5a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -41,6 +41,7 @@
namespace mirror {
class Array;
class Class;
+class IfTable;
class PointerArray;
} // namespace mirror
@@ -50,66 +51,151 @@
// with the last entry being null to make an assembly implementation of a lookup
// faster.
class ImtConflictTable {
+ enum MethodIndex {
+ kMethodInterface,
+ kMethodImplementation,
+ kMethodCount, // Number of elements in enum.
+ };
+
public:
// Build a new table copying `other` and adding the new entry formed of
// the pair { `interface_method`, `implementation_method` }
ImtConflictTable(ImtConflictTable* other,
ArtMethod* interface_method,
- ArtMethod* implementation_method) {
- size_t index = 0;
- while (other->entries_[index].interface_method != nullptr) {
- entries_[index] = other->entries_[index];
- index++;
+ ArtMethod* implementation_method,
+ size_t pointer_size) {
+ const size_t count = other->NumEntries(pointer_size);
+ for (size_t i = 0; i < count; ++i) {
+ SetInterfaceMethod(i, pointer_size, other->GetInterfaceMethod(i, pointer_size));
+ SetImplementationMethod(i, pointer_size, other->GetImplementationMethod(i, pointer_size));
}
- entries_[index].interface_method = interface_method;
- entries_[index].implementation_method = implementation_method;
+ SetInterfaceMethod(count, pointer_size, interface_method);
+ SetImplementationMethod(count, pointer_size, implementation_method);
// Add the null marker.
- entries_[index + 1].interface_method = nullptr;
- entries_[index + 1].implementation_method = nullptr;
+ SetInterfaceMethod(count + 1, pointer_size, nullptr);
+ SetImplementationMethod(count + 1, pointer_size, nullptr);
+ }
+
+ // num_entries excludes the header.
+ ImtConflictTable(size_t num_entries, size_t pointer_size) {
+ SetInterfaceMethod(num_entries, pointer_size, nullptr);
+ SetImplementationMethod(num_entries, pointer_size, nullptr);
+ }
+
+ // Set an entry at an index.
+ void SetInterfaceMethod(size_t index, size_t pointer_size, ArtMethod* method) {
+ SetMethod(index * kMethodCount + kMethodInterface, pointer_size, method);
+ }
+
+ void SetImplementationMethod(size_t index, size_t pointer_size, ArtMethod* method) {
+ SetMethod(index * kMethodCount + kMethodImplementation, pointer_size, method);
+ }
+
+ ArtMethod* GetInterfaceMethod(size_t index, size_t pointer_size) const {
+ return GetMethod(index * kMethodCount + kMethodInterface, pointer_size);
+ }
+
+ ArtMethod* GetImplementationMethod(size_t index, size_t pointer_size) const {
+ return GetMethod(index * kMethodCount + kMethodImplementation, pointer_size);
+ }
+
+ // Visit all of the entries.
+ // NO_THREAD_SAFETY_ANALYSIS for calling with held locks. Visitor is passed a pair of ArtMethod*
+ // and also returns one. The order is <interface, implementation>.
+ template<typename Visitor>
+ void Visit(const Visitor& visitor, size_t pointer_size) NO_THREAD_SAFETY_ANALYSIS {
+ uint32_t table_index = 0;
+ for (;;) {
+ ArtMethod* interface_method = GetInterfaceMethod(table_index, pointer_size);
+ if (interface_method == nullptr) {
+ break;
+ }
+ ArtMethod* implementation_method = GetImplementationMethod(table_index, pointer_size);
+ auto input = std::make_pair(interface_method, implementation_method);
+ std::pair<ArtMethod*, ArtMethod*> updated = visitor(input);
+ if (input.first != updated.first) {
+ SetInterfaceMethod(table_index, pointer_size, updated.first);
+ }
+ if (input.second != updated.second) {
+ SetImplementationMethod(table_index, pointer_size, updated.second);
+ }
+ ++table_index;
+ }
}
// Lookup the implementation ArtMethod associated to `interface_method`. Return null
// if not found.
- ArtMethod* Lookup(ArtMethod* interface_method) const {
+ ArtMethod* Lookup(ArtMethod* interface_method, size_t pointer_size) const {
uint32_t table_index = 0;
- ArtMethod* current_interface_method;
- while ((current_interface_method = entries_[table_index].interface_method) != nullptr) {
- if (current_interface_method == interface_method) {
- return entries_[table_index].implementation_method;
+ for (;;) {
+ ArtMethod* current_interface_method = GetInterfaceMethod(table_index, pointer_size);
+ if (current_interface_method == nullptr) {
+ break;
}
- table_index++;
+ if (current_interface_method == interface_method) {
+ return GetImplementationMethod(table_index, pointer_size);
+ }
+ ++table_index;
}
return nullptr;
}
- // Compute the size in bytes taken by this table.
- size_t ComputeSize() const {
+ // Compute the number of entries in this table.
+ size_t NumEntries(size_t pointer_size) const {
uint32_t table_index = 0;
- size_t total_size = 0;
- while ((entries_[table_index].interface_method) != nullptr) {
- total_size += sizeof(Entry);
- table_index++;
+ while (GetInterfaceMethod(table_index, pointer_size) != nullptr) {
+ ++table_index;
}
+ return table_index;
+ }
+
+ // Compute the size in bytes taken by this table.
+ size_t ComputeSize(size_t pointer_size) const {
// Add the end marker.
- return total_size + sizeof(Entry);
+ return ComputeSize(NumEntries(pointer_size), pointer_size);
}
// Compute the size in bytes needed for copying the given `table` and add
// one more entry.
- static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table) {
- return table->ComputeSize() + sizeof(Entry);
+ static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table, size_t pointer_size) {
+ return table->ComputeSize(pointer_size) + EntrySize(pointer_size);
}
- struct Entry {
- ArtMethod* interface_method;
- ArtMethod* implementation_method;
- };
+ // Compute size with a fixed number of entries.
+ static size_t ComputeSize(size_t num_entries, size_t pointer_size) {
+ return (num_entries + 1) * EntrySize(pointer_size); // Add one for null terminator.
+ }
+
+ static size_t EntrySize(size_t pointer_size) {
+ return pointer_size * static_cast<size_t>(kMethodCount);
+ }
private:
+ ArtMethod* GetMethod(size_t index, size_t pointer_size) const {
+ if (pointer_size == 8) {
+ return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data64_[index]));
+ } else {
+ DCHECK_EQ(pointer_size, 4u);
+ return reinterpret_cast<ArtMethod*>(static_cast<uintptr_t>(data32_[index]));
+ }
+ }
+
+ void SetMethod(size_t index, size_t pointer_size, ArtMethod* method) {
+ if (pointer_size == 8) {
+ data64_[index] = dchecked_integral_cast<uint64_t>(reinterpret_cast<uintptr_t>(method));
+ } else {
+ DCHECK_EQ(pointer_size, 4u);
+ data32_[index] = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(method));
+ }
+ }
+
// Array of entries that the assembly stubs will iterate over. Note that this is
// not fixed size, and we allocate data prior to calling the constructor
// of ImtConflictTable.
- Entry entries_[0];
+ union {
+ uint32_t data32_[0];
+ uint64_t data64_[0];
+ };
DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
};
@@ -220,6 +306,10 @@
return !IsAbstract() && !IsDefaultConflicting();
}
+ bool IsCompilable() {
+ return (GetAccessFlags() & kAccCompileDontBother) == 0;
+ }
+
// A default conflict method is a special sentinel method that stands for a conflict between
// multiple default methods. It cannot be invoked, throwing an IncompatibleClassChangeError if one
// attempts to do so.
@@ -261,6 +351,12 @@
SetAccessFlags(GetAccessFlags() | kAccSkipAccessChecks);
}
+ // Should this method be run in the interpreter and count locks (e.g., failed structured-
+ // locking verification)?
+ bool MustCountLocks() {
+ return (GetAccessFlags() & kAccMustCountLocks) != 0;
+ }
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() SHARED_REQUIRES(Locks::mutator_lock_);
@@ -347,7 +443,6 @@
// Find the method that this method overrides.
ArtMethod* FindOverriddenMethod(size_t pointer_size)
- REQUIRES(Roles::uninterruptible_)
SHARED_REQUIRES(Locks::mutator_lock_);
// Find the method index for this method within other_dexfile. If this method isn't present then
@@ -413,8 +508,8 @@
return reinterpret_cast<ImtConflictTable*>(GetEntryPointFromJniPtrSize(pointer_size));
}
- ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table) {
- SetEntryPointFromJniPtrSize(table, sizeof(void*));
+ ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table, size_t pointer_size) {
+ SetEntryPointFromJniPtrSize(table, pointer_size);
}
ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index d951089..b84e29f 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -162,6 +162,7 @@
MallocArena::MallocArena(size_t size) {
memory_ = reinterpret_cast<uint8_t*>(calloc(1, size));
+ CHECK(memory_ != nullptr); // Abort on OOM.
size_ = size;
}
@@ -319,15 +320,27 @@
// mark only the actually allocated memory as defined. That leaves red zones
// and padding between allocations marked as inaccessible.
size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
- if (UNLIKELY(ptr_ + rounded_bytes > end_)) {
- // Obtain a new block.
- ObtainNewArenaForAllocation(rounded_bytes);
- CHECK(ptr_ != nullptr);
- MEMORY_TOOL_MAKE_NOACCESS(ptr_, end_ - ptr_);
- }
ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
- uint8_t* ret = ptr_;
- ptr_ += rounded_bytes;
+ uint8_t* ret;
+ if (UNLIKELY(rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
+ ret = AllocFromNewArena(rounded_bytes);
+ uint8_t* noaccess_begin = ret + bytes;
+ uint8_t* noaccess_end;
+ if (ret == arena_head_->Begin()) {
+ DCHECK(ptr_ - rounded_bytes == ret);
+ noaccess_end = end_;
+ } else {
+ // We're still using the old arena but `ret` comes from a new one just after it.
+ DCHECK(arena_head_->next_ != nullptr);
+ DCHECK(ret == arena_head_->next_->Begin());
+ DCHECK_EQ(rounded_bytes, arena_head_->next_->GetBytesAllocated());
+ noaccess_end = arena_head_->next_->End();
+ }
+ MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin);
+ } else {
+ ret = ptr_;
+ ptr_ += rounded_bytes;
+ }
MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
// Check that the memory is already zeroed out.
DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; }));
@@ -340,14 +353,27 @@
pool_->FreeArenaChain(arena_head_);
}
-void ArenaAllocator::ObtainNewArenaForAllocation(size_t allocation_size) {
- UpdateBytesAllocated();
- Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, allocation_size));
- new_arena->next_ = arena_head_;
- arena_head_ = new_arena;
- // Update our internal data structures.
- ptr_ = begin_ = new_arena->Begin();
- end_ = new_arena->End();
+uint8_t* ArenaAllocator::AllocFromNewArena(size_t bytes) {
+ Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, bytes));
+ DCHECK(new_arena != nullptr);
+ DCHECK_LE(bytes, new_arena->Size());
+ if (static_cast<size_t>(end_ - ptr_) > new_arena->Size() - bytes) {
+ // The old arena has more space remaining than the new one, so keep using it.
+ // This can happen when the requested size is over half of the default size.
+ DCHECK(arena_head_ != nullptr);
+ new_arena->bytes_allocated_ = bytes; // UpdateBytesAllocated() on the new_arena.
+ new_arena->next_ = arena_head_->next_;
+ arena_head_->next_ = new_arena;
+ } else {
+ UpdateBytesAllocated();
+ new_arena->next_ = arena_head_;
+ arena_head_ = new_arena;
+ // Update our internal data structures.
+ begin_ = new_arena->Begin();
+ ptr_ = begin_ + bytes;
+ end_ = new_arena->End();
+ }
+ return new_arena->Begin();
}
bool ArenaAllocator::Contains(const void* ptr) const {
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 52a1002..6c1a898 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -234,6 +234,8 @@
friend class ScopedArenaAllocator;
template <bool kCount> friend class ArenaAllocatorStatsImpl;
+ friend class ArenaAllocatorTest;
+
private:
DISALLOW_COPY_AND_ASSIGN(Arena);
};
@@ -303,14 +305,10 @@
return AllocWithMemoryTool(bytes, kind);
}
bytes = RoundUp(bytes, kAlignment);
- if (UNLIKELY(ptr_ + bytes > end_)) {
- // Obtain a new block.
- ObtainNewArenaForAllocation(bytes);
- if (UNLIKELY(ptr_ == nullptr)) {
- return nullptr;
- }
- }
ArenaAllocatorStats::RecordAlloc(bytes, kind);
+ if (UNLIKELY(bytes > static_cast<size_t>(end_ - ptr_))) {
+ return AllocFromNewArena(bytes);
+ }
uint8_t* ret = ptr_;
ptr_ += bytes;
return ret;
@@ -350,10 +348,6 @@
return static_cast<T*>(Alloc(length * sizeof(T), kind));
}
- void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
-
- void ObtainNewArenaForAllocation(size_t allocation_size);
-
size_t BytesAllocated() const;
MemStats GetMemStats() const;
@@ -369,6 +363,9 @@
bool Contains(const void* ptr) const;
private:
+ void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
+ uint8_t* AllocFromNewArena(size_t bytes);
+
static constexpr size_t kAlignment = 8;
void UpdateBytesAllocated();
@@ -382,6 +379,8 @@
template <typename U>
friend class ArenaAllocatorAdapter;
+ friend class ArenaAllocatorTest;
+
DISALLOW_COPY_AND_ASSIGN(ArenaAllocator);
}; // ArenaAllocator
diff --git a/runtime/base/arena_allocator_test.cc b/runtime/base/arena_allocator_test.cc
new file mode 100644
index 0000000..9de3cc4
--- /dev/null
+++ b/runtime/base/arena_allocator_test.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/arena_allocator.h"
+#include "base/arena_bit_vector.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class ArenaAllocatorTest : public testing::Test {
+ protected:
+ size_t NumberOfArenas(ArenaAllocator* arena) {
+ size_t result = 0u;
+ for (Arena* a = arena->arena_head_; a != nullptr; a = a->next_) {
+ ++result;
+ }
+ return result;
+ }
+};
+
+TEST_F(ArenaAllocatorTest, Test) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ ArenaBitVector bv(&arena, 10, true);
+ bv.SetBit(5);
+ EXPECT_EQ(1U, bv.GetStorageSize());
+ bv.SetBit(35);
+ EXPECT_EQ(2U, bv.GetStorageSize());
+}
+
+TEST_F(ArenaAllocatorTest, MakeDefined) {
+ // Regression test to make sure we mark the allocated area defined.
+ ArenaPool pool;
+ static constexpr size_t kSmallArraySize = 10;
+ static constexpr size_t kLargeArraySize = 50;
+ uint32_t* small_array;
+ {
+ // Allocate a small array from an arena and release it.
+ ArenaAllocator arena(&pool);
+ small_array = arena.AllocArray<uint32_t>(kSmallArraySize);
+ ASSERT_EQ(0u, small_array[kSmallArraySize - 1u]);
+ }
+ {
+ // Reuse the previous arena and allocate more than previous allocation including red zone.
+ ArenaAllocator arena(&pool);
+ uint32_t* large_array = arena.AllocArray<uint32_t>(kLargeArraySize);
+ ASSERT_EQ(0u, large_array[kLargeArraySize - 1u]);
+ // Verify that the allocation was made on the same arena.
+ ASSERT_EQ(small_array, large_array);
+ }
+}
+
+TEST_F(ArenaAllocatorTest, LargeAllocations) {
+ {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ // Note: Leaving some space for memory tool red zones.
+ void* alloc1 = arena.Alloc(Arena::kDefaultSize * 5 / 8);
+ void* alloc2 = arena.Alloc(Arena::kDefaultSize * 2 / 8);
+ ASSERT_NE(alloc1, alloc2);
+ ASSERT_EQ(1u, NumberOfArenas(&arena));
+ }
+ {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ void* alloc1 = arena.Alloc(Arena::kDefaultSize * 13 / 16);
+ void* alloc2 = arena.Alloc(Arena::kDefaultSize * 11 / 16);
+ ASSERT_NE(alloc1, alloc2);
+ ASSERT_EQ(2u, NumberOfArenas(&arena));
+ void* alloc3 = arena.Alloc(Arena::kDefaultSize * 7 / 16);
+ ASSERT_NE(alloc1, alloc3);
+ ASSERT_NE(alloc2, alloc3);
+ ASSERT_EQ(3u, NumberOfArenas(&arena));
+ }
+ {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ void* alloc1 = arena.Alloc(Arena::kDefaultSize * 13 / 16);
+ void* alloc2 = arena.Alloc(Arena::kDefaultSize * 9 / 16);
+ ASSERT_NE(alloc1, alloc2);
+ ASSERT_EQ(2u, NumberOfArenas(&arena));
+ // Note: Leaving some space for memory tool red zones.
+ void* alloc3 = arena.Alloc(Arena::kDefaultSize * 5 / 16);
+ ASSERT_NE(alloc1, alloc3);
+ ASSERT_NE(alloc2, alloc3);
+ ASSERT_EQ(2u, NumberOfArenas(&arena));
+ }
+ {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ void* alloc1 = arena.Alloc(Arena::kDefaultSize * 9 / 16);
+ void* alloc2 = arena.Alloc(Arena::kDefaultSize * 13 / 16);
+ ASSERT_NE(alloc1, alloc2);
+ ASSERT_EQ(2u, NumberOfArenas(&arena));
+ // Note: Leaving some space for memory tool red zones.
+ void* alloc3 = arena.Alloc(Arena::kDefaultSize * 5 / 16);
+ ASSERT_NE(alloc1, alloc3);
+ ASSERT_NE(alloc2, alloc3);
+ ASSERT_EQ(2u, NumberOfArenas(&arena));
+ }
+ {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ // Note: Leaving some space for memory tool red zones.
+ for (size_t i = 0; i != 15; ++i) {
+ arena.Alloc(Arena::kDefaultSize * 1 / 16); // Allocate 15 times from the same arena.
+ ASSERT_EQ(i + 1u, NumberOfArenas(&arena));
+ arena.Alloc(Arena::kDefaultSize * 17 / 16); // Allocate a separate arena.
+ ASSERT_EQ(i + 2u, NumberOfArenas(&arena));
+ }
+ }
+}
+
+} // namespace art
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 97280c3..3b5b8b5 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -56,6 +56,7 @@
bool threads;
bool verifier;
bool image;
+ bool systrace_lock_logging; // Enabled with "-verbose:sys-locks".
};
// Global log verbosity setting, initialized by InitLogging.
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 7e8a4a4..f3e260b 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -116,9 +116,10 @@
return resolved_method;
}
-inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(Thread* self,
- uint32_t method_idx,
- ArtMethod* referrer) {
+inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(
+ uint32_t method_idx,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader) {
// NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is
// because if we did so than an invoke-super could be incorrectly dispatched in cases where
// GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass
@@ -127,15 +128,11 @@
// interface (either miranda, default or conflict) we would incorrectly assume that is what we
// want to invoke on, instead of the 'concrete' implementation that the direct superclass
// contains.
- mirror::Class* declaring_class = referrer->GetDeclaringClass();
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
- const DexFile* dex_file = h_dex_cache->GetDexFile();
+ const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method = dex_file->GetMethodId(method_idx);
- mirror::Class* resolved_type = h_dex_cache->GetResolvedType(method.class_idx_);
+ mirror::Class* resolved_type = dex_cache->GetResolvedType(method.class_idx_);
if (UNLIKELY(resolved_type == nullptr)) {
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- resolved_type = ResolveType(*dex_file, method.class_idx_, h_dex_cache, class_loader);
+ resolved_type = ResolveType(*dex_file, method.class_idx_, dex_cache, class_loader);
}
return resolved_type;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1e7ee65..35c40cd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -687,6 +687,9 @@
self->AssertNoPendingException();
}
+ // Create conflict tables that depend on the class linker.
+ runtime->FixupConflictTables();
+
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
@@ -773,9 +776,13 @@
bool contains = false;
for (gc::space::ImageSpace* space : spaces) {
auto& header = space->GetImageHeader();
- auto& methods = header.GetMethodsSection();
- auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
- contains |= methods.Contains(offset);
+ size_t offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
+
+ const ImageSection& methods = header.GetMethodsSection();
+ contains = contains || methods.Contains(offset);
+
+ const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
+ contains = contains || runtime_methods.Contains(offset);
}
CHECK(contains) << m << " not found";
}
@@ -1438,20 +1445,14 @@
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*));
+ header.VisitPackedArtMethods(&visitor, space->Begin(), sizeof(void*));
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
}
if (kVerifyArtMethodDeclaringClasses) {
ScopedTrace timing("Verify declaring classes");
ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);
VerifyDeclaringClassVisitor visitor;
- header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
- &visitor,
- space->Begin(),
- sizeof(void*));
+ header.VisitPackedArtMethods(&visitor, space->Begin(), sizeof(void*));
}
return true;
}
@@ -1729,9 +1730,8 @@
// Set entry point to interpreter if in InterpretOnly mode.
if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
- const ImageSection& methods = header.GetMethodsSection();
SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
- methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
+ header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
}
ClassTable* class_table = nullptr;
@@ -1800,10 +1800,7 @@
// This verification needs to happen after the classes have been added to the class loader.
// Since it ensures classes are in the class table.
VerifyClassInTableArtMethodVisitor visitor2(class_table);
- header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
- &visitor2,
- space->Begin(),
- sizeof(void*));
+ header.VisitPackedArtMethods(&visitor2, space->Begin(), sizeof(void*));
}
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
@@ -2035,6 +2032,7 @@
Runtime* const runtime = Runtime::Current();
JavaVMExt* const vm = runtime->GetJavaVM();
vm->DeleteWeakGlobalRef(self, data.weak_root);
+ // Notify the JIT that we need to remove the methods and/or profiling info.
if (runtime->GetJit() != nullptr) {
jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
if (code_cache != nullptr) {
@@ -2752,7 +2750,7 @@
}
if (runtime->IsNativeDebuggable()) {
- DCHECK(runtime->UseJit() && runtime->GetJit()->JitAtFirstUse());
+ DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
// If we are doing native debugging, ignore application's AOT code,
// since we want to JIT it with extra stackmaps for native debugging.
// On the other hand, keep all AOT code from the boot image, since the
@@ -4442,7 +4440,20 @@
// We failed to verify, expect either the klass to be erroneous or verification failed at
// compile time.
if (klass->IsErroneous()) {
- CHECK(self->IsExceptionPending());
+ // The class is erroneous. This may be a verifier error, or another thread attempted
+ // verification and/or initialization and failed. We can distinguish those cases by
+ // whether an exception is already pending.
+ if (self->IsExceptionPending()) {
+ // Check that it's a VerifyError.
+ DCHECK_EQ("java.lang.Class<java.lang.VerifyError>",
+ PrettyClass(self->GetException()->GetClass()));
+ } else {
+ // Check that another thread attempted initialization.
+ DCHECK_NE(0, klass->GetClinitThreadId());
+ DCHECK_NE(self->GetTid(), klass->GetClinitThreadId());
+ // Need to rethrow the previous failure now.
+ ThrowEarlierClassFailure(klass.Get(), true);
+ }
VlogClassInitializationFailure(klass);
} else {
CHECK(Runtime::Current()->IsAotCompiler());
@@ -4452,6 +4463,14 @@
} else {
self->AssertNoPendingException();
}
+
+ // A separate thread could have moved us all the way to initialized. A "simple" example
+ // involves a subclass of the current class being initialized at the same time (which
+ // will implicitly initialize the superclass, if scheduled that way). b/28254258
+ DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus());
+ if (klass->IsInitialized()) {
+ return true;
+ }
}
// If the class is kStatusInitializing, either this thread is
@@ -5940,16 +5959,49 @@
}
}
-// Sets imt_ref appropriately for LinkInterfaceMethods.
-// If there is no method in the imt location of imt_ref it will store the given method there.
-// Otherwise it will set the conflict method which will figure out which method to use during
-// runtime.
-static void SetIMTRef(ArtMethod* unimplemented_method,
- ArtMethod* imt_conflict_method,
- size_t image_pointer_size,
- ArtMethod* current_method,
- /*out*/ArtMethod** imt_ref)
- SHARED_REQUIRES(Locks::mutator_lock_) {
+ArtMethod* ClassLinker::AddMethodToConflictTable(mirror::Class* klass,
+ ArtMethod* conflict_method,
+ ArtMethod* interface_method,
+ ArtMethod* method,
+ bool force_new_conflict_method) {
+ ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+ Runtime* const runtime = Runtime::Current();
+ LinearAlloc* linear_alloc = GetAllocatorForClassLoader(klass->GetClassLoader());
+ bool new_entry = conflict_method == runtime->GetImtConflictMethod() || force_new_conflict_method;
+
+ // Create a new entry if the existing one is the shared conflict method.
+ ArtMethod* new_conflict_method = new_entry
+ ? runtime->CreateImtConflictMethod(linear_alloc)
+ : conflict_method;
+
+ // Allocate a new table. Note that we will leak this table at the next conflict,
+ // but that's a tradeoff compared to making the table fixed size.
+ void* data = linear_alloc->Alloc(
+ Thread::Current(), ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table,
+ image_pointer_size_));
+ if (data == nullptr) {
+ LOG(ERROR) << "Failed to allocate conflict table";
+ return conflict_method;
+ }
+ ImtConflictTable* new_table = new (data) ImtConflictTable(current_table,
+ interface_method,
+ method,
+ image_pointer_size_);
+
+ // Do a fence to ensure threads see the data in the table before it is assigned
+ // to the conflict method.
+ // Note that there is a race in the presence of multiple threads and we may leak
+ // memory from the LinearAlloc, but that's a tradeoff compared to using
+ // atomic operations.
+ QuasiAtomic::ThreadFenceRelease();
+ new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);
+ return new_conflict_method;
+}
+
+void ClassLinker::SetIMTRef(ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ ArtMethod* current_method,
+ /*out*/ArtMethod** imt_ref) {
// Place method in imt if entry is empty, place conflict otherwise.
if (*imt_ref == unimplemented_method) {
*imt_ref = current_method;
@@ -5959,9 +6011,9 @@
// Note that we have checked IsRuntimeMethod, as there may be multiple different
// conflict methods.
MethodNameAndSignatureComparator imt_comparator(
- (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
+ (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size_));
if (imt_comparator.HasSameNameAndSignature(
- current_method->GetInterfaceMethodIfProxy(image_pointer_size))) {
+ current_method->GetInterfaceMethodIfProxy(image_pointer_size_))) {
*imt_ref = current_method;
} else {
*imt_ref = imt_conflict_method;
@@ -5974,6 +6026,151 @@
}
}
+void ClassLinker::FillIMTAndConflictTables(mirror::Class* klass) {
+ DCHECK(klass->ShouldHaveEmbeddedImtAndVTable()) << PrettyClass(klass);
+ DCHECK(!klass->IsTemp()) << PrettyClass(klass);
+ ArtMethod* imt[mirror::Class::kImtSize];
+ Runtime* const runtime = Runtime::Current();
+ ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
+ ArtMethod* const conflict_method = runtime->GetImtConflictMethod();
+ std::fill_n(imt, arraysize(imt), unimplemented_method);
+ if (klass->GetIfTable() != nullptr) {
+ FillIMTFromIfTable(klass->GetIfTable(),
+ unimplemented_method,
+ conflict_method,
+ klass,
+ true,
+ false,
+ &imt[0]);
+ }
+ for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+ klass->SetEmbeddedImTableEntry(i, imt[i], image_pointer_size_);
+ }
+}
+
+static inline uint32_t GetIMTIndex(ArtMethod* interface_method)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ return interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+}
+
+ImtConflictTable* ClassLinker::CreateImtConflictTable(size_t count,
+ LinearAlloc* linear_alloc,
+ size_t image_pointer_size) {
+ void* data = linear_alloc->Alloc(Thread::Current(),
+ ImtConflictTable::ComputeSize(count,
+ image_pointer_size));
+ return (data != nullptr) ? new (data) ImtConflictTable(count, image_pointer_size) : nullptr;
+}
+
+ImtConflictTable* ClassLinker::CreateImtConflictTable(size_t count, LinearAlloc* linear_alloc) {
+ return CreateImtConflictTable(count, linear_alloc, image_pointer_size_);
+}
+
+void ClassLinker::FillIMTFromIfTable(mirror::IfTable* if_table,
+ ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ mirror::Class* klass,
+ bool create_conflict_tables,
+ bool ignore_copied_methods,
+ ArtMethod** imt) {
+ uint32_t conflict_counts[mirror::Class::kImtSize] = {};
+ for (size_t i = 0, length = if_table->Count(); i < length; ++i) {
+ mirror::Class* interface = if_table->GetInterface(i);
+ const size_t num_virtuals = interface->NumVirtualMethods();
+ const size_t method_array_count = if_table->GetMethodArrayCount(i);
+ // Virtual methods can be larger than the if table methods if there are default methods.
+ DCHECK_GE(num_virtuals, method_array_count);
+ if (kIsDebugBuild) {
+ if (klass->IsInterface()) {
+ DCHECK_EQ(method_array_count, 0u);
+ } else {
+ DCHECK_EQ(interface->NumDeclaredVirtualMethods(), method_array_count);
+ }
+ }
+ if (method_array_count == 0) {
+ continue;
+ }
+ auto* method_array = if_table->GetMethodArray(i);
+ for (size_t j = 0; j < method_array_count; ++j) {
+ ArtMethod* implementation_method =
+ method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
+ if (ignore_copied_methods && implementation_method->IsCopied()) {
+ continue;
+ }
+ DCHECK(implementation_method != nullptr);
+ // Miranda methods cannot be used to implement an interface method, but they are safe to put
+ // in the IMT since their entrypoint is the interface trampoline. If we put any copied methods
+ // or interface methods in the IMT here they will not create extra conflicts since we compare
+ // names and signatures in SetIMTRef.
+ ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
+ const uint32_t imt_index = GetIMTIndex(interface_method);
+
+ // There is only any conflicts if all of the interface methods for an IMT slot don't have
+ // the same implementation method, keep track of this to avoid creating a conflict table in
+ // this case.
+
+ // Conflict table size for each IMT slot.
+ ++conflict_counts[imt_index];
+
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ implementation_method,
+ /*out*/&imt[imt_index]);
+ }
+ }
+
+ if (create_conflict_tables) {
+ // Create the conflict tables.
+ LinearAlloc* linear_alloc = GetAllocatorForClassLoader(klass->GetClassLoader());
+ for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+ size_t conflicts = conflict_counts[i];
+ if (imt[i] == imt_conflict_method) {
+ ImtConflictTable* new_table = CreateImtConflictTable(conflicts, linear_alloc);
+ if (new_table != nullptr) {
+ ArtMethod* new_conflict_method =
+ Runtime::Current()->CreateImtConflictMethod(linear_alloc);
+ new_conflict_method->SetImtConflictTable(new_table, image_pointer_size_);
+ imt[i] = new_conflict_method;
+ } else {
+ LOG(ERROR) << "Failed to allocate conflict table";
+ imt[i] = imt_conflict_method;
+ }
+ } else {
+ DCHECK_NE(imt[i], imt_conflict_method);
+ }
+ }
+
+ for (size_t i = 0, length = if_table->Count(); i < length; ++i) {
+ mirror::Class* interface = if_table->GetInterface(i);
+ const size_t method_array_count = if_table->GetMethodArrayCount(i);
+ // Virtual methods can be larger than the if table methods if there are default methods.
+ if (method_array_count == 0) {
+ continue;
+ }
+ auto* method_array = if_table->GetMethodArray(i);
+ for (size_t j = 0; j < method_array_count; ++j) {
+ ArtMethod* implementation_method =
+ method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
+ if (ignore_copied_methods && implementation_method->IsCopied()) {
+ continue;
+ }
+ DCHECK(implementation_method != nullptr);
+ ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
+ const uint32_t imt_index = GetIMTIndex(interface_method);
+ if (!imt[imt_index]->IsRuntimeMethod() ||
+ imt[imt_index] == unimplemented_method ||
+ imt[imt_index] == imt_conflict_method) {
+ continue;
+ }
+ ImtConflictTable* table = imt[imt_index]->GetImtConflictTable(image_pointer_size_);
+ const size_t num_entries = table->NumEntries(image_pointer_size_);
+ table->SetInterfaceMethod(num_entries, image_pointer_size_, interface_method);
+ table->SetImplementationMethod(num_entries, image_pointer_size_, implementation_method);
+ }
+ }
+ }
+}
+
// Simple helper function that checks that no subtypes of 'val' are contained within the 'classes'
// set.
static bool NotSubinterfaceOfAny(const std::unordered_set<mirror::Class*>& classes,
@@ -6209,48 +6406,28 @@
}
}
-static void FillImtFromSuperClass(Handle<mirror::Class> klass,
- Handle<mirror::IfTable> iftable,
- ArtMethod* unimplemented_method,
- ArtMethod* imt_conflict_method,
- ArtMethod** out_imt,
- size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_) {
+void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
+ ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ ArtMethod** imt) {
DCHECK(klass->HasSuperClass());
mirror::Class* super_class = klass->GetSuperClass();
if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
- out_imt[i] = super_class->GetEmbeddedImTableEntry(i, pointer_size);
+ imt[i] = super_class->GetEmbeddedImTableEntry(i, image_pointer_size_);
}
} else {
// No imt in the super class, need to reconstruct from the iftable.
mirror::IfTable* if_table = super_class->GetIfTable();
- const size_t length = super_class->GetIfTableCount();
- for (size_t i = 0; i < length; ++i) {
- mirror::Class* interface = iftable->GetInterface(i);
- const size_t num_virtuals = interface->NumDeclaredVirtualMethods();
- const size_t method_array_count = if_table->GetMethodArrayCount(i);
- DCHECK_EQ(num_virtuals, method_array_count);
- if (method_array_count == 0) {
- continue;
- }
- auto* method_array = if_table->GetMethodArray(i);
- for (size_t j = 0; j < num_virtuals; ++j) {
- auto method = method_array->GetElementPtrSize<ArtMethod*>(j, pointer_size);
- DCHECK(method != nullptr) << PrettyClass(super_class);
- // Miranda methods cannot be used to implement an interface method and defaults should be
- // skipped in case we override it.
- if (method->IsDefault() || method->IsMiranda()) {
- continue;
- }
- ArtMethod* interface_method = interface->GetVirtualMethod(j, pointer_size);
- uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
- auto** imt_ref = &out_imt[imt_index];
- if (*imt_ref == unimplemented_method) {
- *imt_ref = method;
- } else if (*imt_ref != imt_conflict_method) {
- *imt_ref = imt_conflict_method;
- }
- }
+ if (if_table != nullptr) {
+ // Ignore copied methods since we will handle these in LinkInterfaceMethods.
+ FillIMTFromIfTable(if_table,
+ unimplemented_method,
+ imt_conflict_method,
+ klass.Get(),
+ /*create_conflict_table*/false,
+ /*ignore_copied_methods*/true,
+ /*out*/imt);
}
}
}
@@ -6293,13 +6470,10 @@
const bool extend_super_iftable = has_superclass;
if (has_superclass && fill_tables) {
FillImtFromSuperClass(klass,
- iftable,
unimplemented_method,
imt_conflict_method,
- out_imt,
- image_pointer_size_);
+ out_imt);
}
-
// Allocate method arrays before since we don't want miss visiting miranda method roots due to
// thread suspension.
if (fill_tables) {
@@ -6383,7 +6557,7 @@
auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
MethodNameAndSignatureComparator interface_name_comparator(
interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
- uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ uint32_t imt_index = GetIMTIndex(interface_method);
ArtMethod** imt_ptr = &out_imt[imt_index];
// For each method listed in the interface's method list, find the
// matching method in our class's method list. We want to favor the
@@ -6428,7 +6602,6 @@
// Place method in imt if entry is empty, place conflict otherwise.
SetIMTRef(unimplemented_method,
imt_conflict_method,
- image_pointer_size_,
vtable_method,
/*out*/imt_ptr);
}
@@ -6462,6 +6635,17 @@
// The method is not overridable by a default method (i.e. it is directly implemented
// in some class). Therefore move onto the next interface method.
continue;
+ } else {
+ // If the super-classes method is override-able by a default method we need to keep
+ // track of it since though it is override-able it is not guaranteed to be 'overridden'.
+ // If it turns out not to be overridden and we did not keep track of it we might add it
+ // to the vtable twice, causing corruption (vtable entries having inconsistent and
+ // illegal states, incorrect vtable size, and incorrect or inconsistent iftable entries)
+ // in this class and any subclasses.
+ DCHECK(vtable_impl == nullptr || vtable_impl == supers_method)
+ << "vtable_impl was " << PrettyMethod(vtable_impl) << " and not 'nullptr' or "
+ << PrettyMethod(supers_method) << " as expected. IFTable appears to be corrupt!";
+ vtable_impl = supers_method;
}
}
// If we haven't found it yet we should search through the interfaces for default methods.
@@ -6560,7 +6744,6 @@
method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
SetIMTRef(unimplemented_method,
imt_conflict_method,
- image_pointer_size_,
current_method,
/*out*/imt_ptr);
}
@@ -7814,6 +7997,7 @@
VLOG(class_linker) << "Collecting class profile for dex file " << location
<< " types=" << num_types << " class_defs=" << num_class_defs;
DexCacheResolvedClasses resolved_classes(dex_file->GetLocation(),
+ dex_file->GetBaseLocation(),
dex_file->GetLocationChecksum());
size_t num_resolved = 0;
std::unordered_set<uint16_t> class_set;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5de502b..ece171c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -53,6 +53,7 @@
class StackTraceElement;
} // namespace mirror
+class ImtConflictTable;
template<class T> class Handle;
template<class T> class MutableHandle;
class InternTable;
@@ -310,9 +311,9 @@
// This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be
// different then the declaring class of the resolved method due to copied
// miranda/default/conflict methods.
- mirror::Class* ResolveReferencedClassOfMethod(Thread* self,
- uint32_t method_idx,
- ArtMethod* referrer)
+ mirror::Class* ResolveReferencedClassOfMethod(uint32_t method_idx,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_, !Roles::uninterruptible_);
template <ResolveMode kResolveMode>
@@ -610,6 +611,26 @@
const std::set<DexCacheResolvedClasses>& classes)
REQUIRES(!dex_lock_);
+ ArtMethod* AddMethodToConflictTable(mirror::Class* klass,
+ ArtMethod* conflict_method,
+ ArtMethod* interface_method,
+ ArtMethod* method,
+ bool force_new_conflict_method)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Create a conflict table with a specified capacity.
+ ImtConflictTable* CreateImtConflictTable(size_t count, LinearAlloc* linear_alloc);
+
+ // Static version for when the class linker is not yet created.
+ static ImtConflictTable* CreateImtConflictTable(size_t count,
+ LinearAlloc* linear_alloc,
+ size_t pointer_size);
+
+
+ // Create the IMT and conflict tables for a class.
+ void FillIMTAndConflictTables(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
+
+
struct DexCacheData {
// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
@@ -1057,6 +1078,28 @@
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Sets imt_ref appropriately for LinkInterfaceMethods.
+ // If there is no method in the imt location of imt_ref it will store the given method there.
+ // Otherwise it will set the conflict method which will figure out which method to use during
+ // runtime.
+ void SetIMTRef(ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ ArtMethod* current_method,
+ /*out*/ArtMethod** imt_ref) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void FillIMTFromIfTable(mirror::IfTable* if_table,
+ ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ mirror::Class* klass,
+ bool create_conflict_tables,
+ bool ignore_copied_methods,
+ ArtMethod** imt) SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void FillImtFromSuperClass(Handle<mirror::Class> klass,
+ ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ ArtMethod** imt) SHARED_REQUIRES(Locks::mutator_lock_);
+
std::vector<const DexFile*> boot_class_path_;
std::vector<std::unique_ptr<const DexFile>> boot_dex_files_;
diff --git a/runtime/dex_cache_resolved_classes.h b/runtime/dex_cache_resolved_classes.h
index 80c12cb..0febbed 100644
--- a/runtime/dex_cache_resolved_classes.h
+++ b/runtime/dex_cache_resolved_classes.h
@@ -26,8 +26,11 @@
// Data structure for passing around which classes belonging to a dex cache / dex file are resolved.
class DexCacheResolvedClasses {
public:
- DexCacheResolvedClasses(const std::string& dex_location, uint32_t location_checksum)
+ DexCacheResolvedClasses(const std::string& dex_location,
+ const std::string& base_location,
+ uint32_t location_checksum)
: dex_location_(dex_location),
+ base_location_(base_location),
location_checksum_(location_checksum) {}
// Only compare the key elements, ignore the resolved classes.
@@ -35,6 +38,7 @@
if (location_checksum_ != other.location_checksum_) {
return static_cast<int>(location_checksum_ - other.location_checksum_);
}
+ // Don't need to compare base_location_ since dex_location_ has more info.
return dex_location_.compare(other.dex_location_);
}
@@ -47,6 +51,10 @@
return dex_location_;
}
+ const std::string& GetBaseLocation() const {
+ return base_location_;
+ }
+
uint32_t GetLocationChecksum() const {
return location_checksum_;
}
@@ -57,6 +65,7 @@
private:
const std::string dex_location_;
+ const std::string base_location_;
const uint32_t location_checksum_;
// Array of resolved class def indexes.
mutable std::unordered_set<uint16_t> classes_;
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 4e01d80..f3a0d2f 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -77,6 +77,10 @@
extern "C" void art_quick_lock_object(art::mirror::Object*);
extern "C" void art_quick_unlock_object(art::mirror::Object*);
+// Lock entrypoints that do not inline any behavior (e.g., thin-locks).
+extern "C" void art_quick_lock_object_no_inline(art::mirror::Object*);
+extern "C" void art_quick_unlock_object_no_inline(art::mirror::Object*);
+
// Math entrypoints.
extern "C" int64_t art_quick_d2l(double);
extern "C" int64_t art_quick_f2l(float);
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
new file mode 100644
index 0000000..5dafa8b
--- /dev/null
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_
+#define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_
+
+#include "base/logging.h"
+#include "entrypoints/jni/jni_entrypoints.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "quick_alloc_entrypoints.h"
+#include "quick_default_externs.h"
+#include "quick_entrypoints.h"
+
+namespace art {
+
+void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
+ // JNI
+ jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub;
+
+ // Alloc
+ ResetQuickAllocEntryPoints(qpoints);
+
+ // DexCache
+ qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
+ qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
+ qpoints->pInitializeType = art_quick_initialize_type;
+ qpoints->pResolveString = art_quick_resolve_string;
+
+ // Field
+ qpoints->pSet8Instance = art_quick_set8_instance;
+ qpoints->pSet8Static = art_quick_set8_static;
+ qpoints->pSet16Instance = art_quick_set16_instance;
+ qpoints->pSet16Static = art_quick_set16_static;
+ qpoints->pSet32Instance = art_quick_set32_instance;
+ qpoints->pSet32Static = art_quick_set32_static;
+ qpoints->pSet64Instance = art_quick_set64_instance;
+ qpoints->pSet64Static = art_quick_set64_static;
+ qpoints->pSetObjInstance = art_quick_set_obj_instance;
+ qpoints->pSetObjStatic = art_quick_set_obj_static;
+ qpoints->pGetByteInstance = art_quick_get_byte_instance;
+ qpoints->pGetBooleanInstance = art_quick_get_boolean_instance;
+ qpoints->pGetShortInstance = art_quick_get_short_instance;
+ qpoints->pGetCharInstance = art_quick_get_char_instance;
+ qpoints->pGet32Instance = art_quick_get32_instance;
+ qpoints->pGet64Instance = art_quick_get64_instance;
+ qpoints->pGetObjInstance = art_quick_get_obj_instance;
+ qpoints->pGetByteStatic = art_quick_get_byte_static;
+ qpoints->pGetBooleanStatic = art_quick_get_boolean_static;
+ qpoints->pGetShortStatic = art_quick_get_short_static;
+ qpoints->pGetCharStatic = art_quick_get_char_static;
+ qpoints->pGet32Static = art_quick_get32_static;
+ qpoints->pGet64Static = art_quick_get64_static;
+ qpoints->pGetObjStatic = art_quick_get_obj_static;
+
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
+ qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
+
+ // JNI
+ qpoints->pJniMethodStart = JniMethodStart;
+ qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized;
+ qpoints->pJniMethodEnd = JniMethodEnd;
+ qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized;
+ qpoints->pJniMethodEndWithReference = JniMethodEndWithReference;
+ qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized;
+ qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline;
+
+ // Locks
+ if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
+ qpoints->pLockObject = art_quick_lock_object_no_inline;
+ qpoints->pUnlockObject = art_quick_unlock_object_no_inline;
+ } else {
+ qpoints->pLockObject = art_quick_lock_object;
+ qpoints->pUnlockObject = art_quick_unlock_object;
+ }
+
+ // Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
+ qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
+ qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
+ qpoints->pInvokeDirectTrampolineWithAccessCheck =
+ art_quick_invoke_direct_trampoline_with_access_check;
+ qpoints->pInvokeInterfaceTrampolineWithAccessCheck =
+ art_quick_invoke_interface_trampoline_with_access_check;
+ qpoints->pInvokeStaticTrampolineWithAccessCheck =
+ art_quick_invoke_static_trampoline_with_access_check;
+ qpoints->pInvokeSuperTrampolineWithAccessCheck =
+ art_quick_invoke_super_trampoline_with_access_check;
+ qpoints->pInvokeVirtualTrampolineWithAccessCheck =
+ art_quick_invoke_virtual_trampoline_with_access_check;
+
+ // Thread
+ qpoints->pTestSuspend = art_quick_test_suspend;
+
+ // Throws
+ qpoints->pDeliverException = art_quick_deliver_exception;
+ qpoints->pThrowArrayBounds = art_quick_throw_array_bounds;
+ qpoints->pThrowDivZero = art_quick_throw_div_zero;
+ qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
+ qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
+ qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+
+ // Deoptimize
+ qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index f3e8dba..e9cdbb7 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1038,9 +1038,14 @@
} else {
DCHECK_EQ(invoke_type, kSuper);
CHECK(caller != nullptr) << invoke_type;
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(caller->GetDeclaringClass()->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader()));
// TODO Maybe put this into a mirror::Class function.
mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod(
- self, called_method.dex_method_index, caller);
+ called_method.dex_method_index, dex_cache, class_loader);
if (ref_class->IsInterface()) {
called = ref_class->FindVirtualMethodForInterfaceSuper(called, sizeof(void*));
} else {
@@ -2169,7 +2174,8 @@
imt_index % mirror::Class::kImtSize, sizeof(void*));
if (LIKELY(conflict_method->IsRuntimeMethod())) {
ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
- method = current_table->Lookup(interface_method);
+ DCHECK(current_table != nullptr);
+ method = current_table->Lookup(interface_method, sizeof(void*));
} else {
// It seems we aren't really a conflict method!
method = cls->FindVirtualMethodForInterface(interface_method, sizeof(void*));
@@ -2220,34 +2226,13 @@
ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
imt_index % mirror::Class::kImtSize, sizeof(void*));
if (conflict_method->IsRuntimeMethod()) {
- ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
- Runtime* runtime = Runtime::Current();
- LinearAlloc* linear_alloc = (cls->GetClassLoader() == nullptr)
- ? runtime->GetLinearAlloc()
- : cls->GetClassLoader()->GetAllocator();
- bool is_new_entry = (conflict_method == runtime->GetImtConflictMethod());
-
- // Create a new entry if the existing one is the shared conflict method.
- ArtMethod* new_conflict_method = is_new_entry
- ? runtime->CreateImtConflictMethod(linear_alloc)
- : conflict_method;
-
- // Allocate a new table. Note that we will leak this table at the next conflict,
- // but that's a tradeoff compared to making the table fixed size.
- void* data = linear_alloc->Alloc(
- self, ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table));
- CHECK(data != nullptr) << "Out of memory";
- ImtConflictTable* new_table = new (data) ImtConflictTable(
- current_table, interface_method, method);
-
- // Do a fence to ensure threads see the data in the table before it is assigned
- // to the conlict method.
- // Note that there is a race in the presence of multiple threads and we may leak
- // memory from the LinearAlloc, but that's a tradeoff compared to using
- // atomic operations.
- QuasiAtomic::ThreadFenceRelease();
- new_conflict_method->SetImtConflictTable(new_table);
- if (is_new_entry) {
+ ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
+ cls.Get(),
+ conflict_method,
+ interface_method,
+ method,
+ /*force_new_conflict_method*/false);
+ if (new_conflict_method != conflict_method) {
// Update the IMT if we create a new conflict method. No fence needed here, as the
// data is consistent.
cls->SetEmbeddedImTableEntry(imt_index % mirror::Class::kImtSize,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index c2f772f..df5aa0a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2687,8 +2687,8 @@
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
}
- if ((gc_type == collector::kGcTypeFull) && runtime->UseJit()) {
- // It's time to clear all inline caches, in case some classes can be unloaded.
+ // It's time to clear all inline caches, in case some classes can be unloaded.
+ if ((gc_type == collector::kGcTypeFull) && (runtime->GetJit() != nullptr)) {
runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self);
}
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index d386c74..78c570f 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -880,7 +880,7 @@
class ForwardObjectAdapter {
public:
- ALWAYS_INLINE ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
+ ALWAYS_INLINE explicit ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
template <typename T>
ALWAYS_INLINE T* operator()(T* src) const {
@@ -893,7 +893,7 @@
class ForwardCodeAdapter {
public:
- ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor)
+ ALWAYS_INLINE explicit ForwardCodeAdapter(const FixupVisitor* visitor)
: visitor_(visitor) {}
template <typename T>
@@ -914,10 +914,26 @@
pointer_size_(pointer_size) {}
virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
- if (fixup_heap_objects_) {
- method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+ // TODO: Separate visitor for runtime vs normal methods.
+ if (UNLIKELY(method->IsRuntimeMethod())) {
+ ImtConflictTable* table = method->GetImtConflictTable(pointer_size_);
+ if (table != nullptr) {
+ ImtConflictTable* new_table = ForwardObject(table);
+ if (table != new_table) {
+ method->SetImtConflictTable(new_table, pointer_size_);
+ }
+ }
+ const void* old_code = method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
+ const void* new_code = ForwardCode(old_code);
+ if (old_code != new_code) {
+ method->SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size_);
+ }
+ } else {
+ if (fixup_heap_objects_) {
+ method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+ }
+ method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
}
- method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
}
private:
@@ -1018,6 +1034,7 @@
const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects);
uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+ FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
if (fixup_image) {
// Two pass approach, fix up all classes first, then fix up non class-objects.
// The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
@@ -1037,7 +1054,6 @@
ScopedObjectAccess soa(Thread::Current());
timing.NewTiming("Fixup objects");
bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
- FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
// Fixup image roots.
CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
image_header.GetImageRoots<kWithoutReadBarrier>())));
@@ -1104,19 +1120,18 @@
boot_oat,
app_image,
app_oat);
- image_header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
- &method_visitor,
- target_base,
- pointer_size);
+ image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size);
}
if (fixup_image) {
{
// Only touches objects in the app image, no need for mutator lock.
TimingLogger::ScopedTiming timing("Fixup fields", &logger);
FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat);
- image_header.GetImageSection(ImageHeader::kSectionArtFields).VisitPackedArtFields(
- &field_visitor,
- target_base);
+ image_header.VisitPackedArtFields(&field_visitor, target_base);
+ }
+ {
+ TimingLogger::ScopedTiming timing("Fixup conflict tables", &logger);
+ image_header.VisitPackedImtConflictTables(fixup_adapter, target_base, pointer_size);
}
// In the app image case, the image methods are actually in the boot image.
image_header.RelocateImageMethods(boot_image.Delta());
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 3885c60..9895395 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -505,6 +505,7 @@
// Walk the roots and the heap.
output_->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime);
+ simple_roots_.clear();
runtime->VisitRoots(this);
runtime->VisitImageRoots(this);
runtime->GetHeap()->VisitObjectsPaused(VisitObjectCallback, this);
@@ -884,6 +885,14 @@
gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>> frames_;
std::unordered_map<const mirror::Object*, const gc::AllocRecordStackTrace*> allocation_records_;
+ // Set used to keep track of what simple root records we have already
+ // emitted, to avoid emitting duplicate entries. The simple root records are
+ // those that contain no other information than the root type and the object
+ // id. A pair of root type and object id is packed into a uint64_t, with
+ // the root type in the upper 32 bits and the object id in the lower 32
+ // bits.
+ std::unordered_set<uint64_t> simple_roots_;
+
friend class GcRootVisitor;
DISALLOW_COPY_AND_ASSIGN(Hprof);
};
@@ -962,10 +971,14 @@
case HPROF_ROOT_MONITOR_USED:
case HPROF_ROOT_INTERNED_STRING:
case HPROF_ROOT_DEBUGGER:
- case HPROF_ROOT_VM_INTERNAL:
- __ AddU1(heap_tag);
- __ AddObjectId(obj);
+ case HPROF_ROOT_VM_INTERNAL: {
+ uint64_t key = (static_cast<uint64_t>(heap_tag) << 32) | PointerToLowMemUInt32(obj);
+ if (simple_roots_.insert(key).second) {
+ __ AddU1(heap_tag);
+ __ AddObjectId(obj);
+ }
break;
+ }
// ID: object ID
// ID: JNI global ref ID
diff --git a/runtime/image-inl.h b/runtime/image-inl.h
index e3307d8..ea75a62 100644
--- a/runtime/image-inl.h
+++ b/runtime/image-inl.h
@@ -19,6 +19,8 @@
#include "image.h"
+#include "art_method.h"
+
namespace art {
template <ReadBarrierOption kReadBarrierOption>
@@ -42,6 +44,20 @@
return image_roots;
}
+template <typename Visitor>
+inline void ImageHeader::VisitPackedImtConflictTables(const Visitor& visitor,
+ uint8_t* base,
+ size_t pointer_size) const {
+ const ImageSection& section = GetImageSection(kSectionIMTConflictTables);
+ for (size_t pos = 0; pos < section.Size(); ) {
+ auto* table = reinterpret_cast<ImtConflictTable*>(base + section.Offset() + pos);
+ table->Visit([&visitor](const std::pair<ArtMethod*, ArtMethod*>& methods) {
+ return std::make_pair(visitor(methods.first), visitor(methods.second));
+ }, pointer_size);
+ pos += table->ComputeSize(pointer_size);
+ }
+}
+
} // namespace art
#endif // ART_RUNTIME_IMAGE_INL_H_
diff --git a/runtime/image.cc b/runtime/image.cc
index 1f54e3e..a9552c2 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '7', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '9', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
@@ -147,9 +147,10 @@
return os << "size=" << section.Size() << " range=" << section.Offset() << "-" << section.End();
}
-void ImageSection::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const {
- for (size_t pos = 0; pos < Size(); ) {
- auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + Offset() + pos);
+void ImageHeader::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const {
+ const ImageSection& fields = GetFieldsSection();
+ for (size_t pos = 0; pos < fields.Size(); ) {
+ auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + fields.Offset() + pos);
for (size_t i = 0; i < array->size(); ++i) {
visitor->Visit(&array->At(i, sizeof(ArtField)));
}
@@ -157,18 +158,25 @@
}
}
-void ImageSection::VisitPackedArtMethods(ArtMethodVisitor* visitor,
- uint8_t* base,
- size_t pointer_size) const {
+void ImageHeader::VisitPackedArtMethods(ArtMethodVisitor* visitor,
+ uint8_t* base,
+ size_t pointer_size) const {
const size_t method_alignment = ArtMethod::Alignment(pointer_size);
const size_t method_size = ArtMethod::Size(pointer_size);
- for (size_t pos = 0; pos < Size(); ) {
- auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + Offset() + pos);
+ const ImageSection& methods = GetMethodsSection();
+ for (size_t pos = 0; pos < methods.Size(); ) {
+ auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + methods.Offset() + pos);
for (size_t i = 0; i < array->size(); ++i) {
visitor->Visit(&array->At(i, method_size, method_alignment));
}
pos += array->ComputeSize(array->size(), method_size, method_alignment);
}
+ const ImageSection& runtime_methods = GetRuntimeMethodsSection();
+ for (size_t pos = 0; pos < runtime_methods.Size(); ) {
+ auto* method = reinterpret_cast<ArtMethod*>(base + runtime_methods.Offset() + pos);
+ visitor->Visit(method);
+ pos += method_size;
+ }
}
} // namespace art
diff --git a/runtime/image.h b/runtime/image.h
index 8e5dbad..2ea9af7 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -64,12 +64,6 @@
return offset - offset_ < size_;
}
- // Visit ArtMethods in the section starting at base.
- void VisitPackedArtMethods(ArtMethodVisitor* visitor, uint8_t* base, size_t pointer_size) const;
-
- // Visit ArtMethods in the section starting at base.
- void VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const;
-
private:
uint32_t offset_;
uint32_t size_;
@@ -200,6 +194,8 @@
kSectionObjects,
kSectionArtFields,
kSectionArtMethods,
+ kSectionRuntimeMethods,
+ kSectionIMTConflictTables,
kSectionDexCacheArrays,
kSectionInternedStrings,
kSectionClassTable,
@@ -211,10 +207,19 @@
void SetImageMethod(ImageMethod index, ArtMethod* method);
const ImageSection& GetImageSection(ImageSections index) const;
+
const ImageSection& GetMethodsSection() const {
return GetImageSection(kSectionArtMethods);
}
+ const ImageSection& GetRuntimeMethodsSection() const {
+ return GetImageSection(kSectionRuntimeMethods);
+ }
+
+ const ImageSection& GetFieldsSection() const {
+ return GetImageSection(ImageHeader::kSectionArtFields);
+ }
+
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
mirror::Object* GetImageRoot(ImageRoot image_root) const
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -265,6 +270,19 @@
return boot_image_size_ != 0u;
}
+ // Visit ArtMethods in the section starting at base. Includes runtime methods.
+ // TODO: Delete base parameter if it is always equal to GetImageBegin.
+ void VisitPackedArtMethods(ArtMethodVisitor* visitor, uint8_t* base, size_t pointer_size) const;
+
+ // Visit ArtMethods in the section starting at base.
+ // TODO: Delete base parameter if it is always equal to GetImageBegin.
+ void VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const;
+
+ template <typename Visitor>
+ void VisitPackedImtConflictTables(const Visitor& visitor,
+ uint8_t* base,
+ size_t pointer_size) const;
+
private:
static const uint8_t kImageMagic[4];
static const uint8_t kImageVersion[4];
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 97dbe5d..81a396a 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -303,8 +303,13 @@
shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
+ // Lock counting is a special version of accessibility checks, and for simplicity and
+ // reduction of template parameters, we gate it behind access-checks mode.
+ ArtMethod* method = shadow_frame.GetMethod();
+ DCHECK(!method->SkipAccessChecks() || !method->MustCountLocks());
+
bool transaction_active = Runtime::Current()->IsActiveTransaction();
- if (LIKELY(shadow_frame.GetMethod()->SkipAccessChecks())) {
+ if (LIKELY(method->SkipAccessChecks())) {
// Enter the "without access check" interpreter.
if (kInterpreterImplKind == kMterpImplKind) {
if (transaction_active) {
@@ -487,6 +492,10 @@
// Are we executing the first shadow frame?
bool first = true;
while (shadow_frame != nullptr) {
+ // We do not want to recover lock state for lock counting when deoptimizing. Currently,
+ // the compiler should not have compiled a method that failed structured-locking checks.
+ DCHECK(!shadow_frame->GetMethod()->MustCountLocks());
+
self->SetTopOfShadowStack(shadow_frame);
const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
const uint32_t dex_pc = shadow_frame->GetDexPC();
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index e5b89e2..69376fd 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -95,7 +95,9 @@
StackHandleScope<1> hs(self);
Handle<Object> h_ref(hs.NewHandle(ref));
h_ref->MonitorEnter(self);
- frame->GetLockCountData().AddMonitor<kMonitorCounting>(self, h_ref.Get());
+ if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+ frame->GetLockCountData().AddMonitor(self, h_ref.Get());
+ }
}
template <bool kMonitorCounting>
@@ -107,7 +109,19 @@
StackHandleScope<1> hs(self);
Handle<Object> h_ref(hs.NewHandle(ref));
h_ref->MonitorExit(self);
- frame->GetLockCountData().RemoveMonitorOrThrow<kMonitorCounting>(self, h_ref.Get());
+ if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+ frame->GetLockCountData().RemoveMonitorOrThrow(self, h_ref.Get());
+ }
+}
+
+template <bool kMonitorCounting>
+static inline bool DoMonitorCheckOnExit(Thread* self, ShadowFrame* frame)
+ NO_THREAD_SAFETY_ANALYSIS
+ REQUIRES(!Roles::uninterruptible_) {
+ if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
+ return frame->GetLockCountData().CheckAllMonitorsReleasedOrThrow(self);
+ }
+ return true;
}
void AbortTransactionF(Thread* self, const char* fmt, ...)
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index 13cfb98..f03036b 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -104,8 +104,7 @@
} HANDLE_INSTRUCTION_END();
#define HANDLE_MONITOR_CHECKS() \
- if (!shadow_frame.GetLockCountData(). \
- CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self)) { \
+ if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) { \
HANDLE_PENDING_EXCEPTION(); \
}
@@ -2584,7 +2583,7 @@
instrumentation);
if (found_dex_pc == DexFile::kDexNoIndex) {
// Structured locking is to be enforced for abnormal termination, too.
- shadow_frame.GetLockCountData().CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self);
+ DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);
return JValue(); /* Handled in caller. */
} else {
int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc);
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 4323d4f..18330ba 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -34,8 +34,7 @@
instrumentation); \
if (found_dex_pc == DexFile::kDexNoIndex) { \
/* Structured locking is to be enforced for abnormal termination, too. */ \
- shadow_frame.GetLockCountData(). \
- CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self); \
+ DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame); \
if (interpret_one_instruction) { \
/* Signal mterp to return to caller */ \
shadow_frame.SetDexPC(DexFile::kDexNoIndex); \
@@ -57,8 +56,7 @@
} while (false)
#define HANDLE_MONITOR_CHECKS() \
- if (!shadow_frame.GetLockCountData(). \
- CheckAllMonitorsReleasedOrThrow<do_assignability_check>(self)) { \
+ if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) { \
HANDLE_PENDING_EXCEPTION(); \
}
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index e46f9cd..f78e1bc 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -12985,6 +12985,7 @@
* not-taken path. All Dalvik not-taken conditional branch offsets are 2.
*/
.L_check_not_taken_osr:
+ EXPORT_PC
movl rSELF, %eax
movl %eax, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %ecx
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index 62dce6e..031cec8 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -11961,6 +11961,7 @@
* not-taken path. All Dalvik not-taken conditional branch offsets are 2.
*/
.L_check_not_taken_osr:
+ EXPORT_PC
movq rSELF, OUT_ARG0
leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
movl $2, OUT_32_ARG2
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index fa03e78..e8c8ca8 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -234,6 +234,7 @@
* not-taken path. All Dalvik not-taken conditional branch offsets are 2.
*/
.L_check_not_taken_osr:
+ EXPORT_PC
movl rSELF, %eax
movl %eax, OUT_ARG0(%esp)
leal OFF_FP_SHADOWFRAME(rFP), %ecx
diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S
index 54d0cb1..f78f163 100644
--- a/runtime/interpreter/mterp/x86_64/footer.S
+++ b/runtime/interpreter/mterp/x86_64/footer.S
@@ -213,6 +213,7 @@
* not-taken path. All Dalvik not-taken conditional branch offsets are 2.
*/
.L_check_not_taken_osr:
+ EXPORT_PC
movq rSELF, OUT_ARG0
leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1
movl $$2, OUT_32_ARG2
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index a41fd45..79c3203 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -318,6 +318,7 @@
}
JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
delete raw_vm->GetRuntime();
+ android::ResetNativeLoader();
return JNI_OK;
}
@@ -950,6 +951,11 @@
LOG(WARNING) << "CreateJavaVM failed";
return JNI_ERR;
}
+
+ // Initialize native loader. This step makes sure we have
+ // everything set up before we start using JNI.
+ android::InitializeNativeLoader();
+
*p_env = Thread::Current()->GetJniEnv();
*p_vm = runtime->GetJavaVM();
return JNI_OK;
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 2a66847..e9317a5 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -51,7 +51,7 @@
JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) {
auto* jit_options = new JitOptions;
- jit_options->use_jit_ = options.GetOrDefault(RuntimeArgumentMap::UseJIT);
+ jit_options->use_jit_compilation_ = options.GetOrDefault(RuntimeArgumentMap::UseJitCompilation);
jit_options->code_cache_initial_capacity_ =
options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheInitialCapacity);
@@ -102,14 +102,26 @@
static_cast<size_t>(1));
}
+ if (options.Exists(RuntimeArgumentMap::JITInvokeTransitionWeight)) {
+ if (jit_options->invoke_transition_weight_ > jit_options->warmup_threshold_) {
+ LOG(FATAL) << "Invoke transition weight is above the warmup threshold.";
+ } else if (jit_options->invoke_transition_weight_ == 0) {
+ LOG(FATAL) << "Invoke transition ratio cannot be 0.";
+ }
+ jit_options->invoke_transition_weight_ =
+ *options.Get(RuntimeArgumentMap::JITInvokeTransitionWeight);
+ } else {
+ jit_options->invoke_transition_weight_ = std::max(
+ jit_options->warmup_threshold_ / Jit::kDefaultInvokeTransitionWeightRatio,
+ static_cast<size_t>(1));;
+ }
+
return jit_options;
}
bool Jit::ShouldUsePriorityThreadWeight() {
- // TODO(calin): verify that IsSensitiveThread covers only the cases we are interested on.
- // In particular if apps can set StrictMode policies for any of their threads, case in which
- // we need to find another way to track sensitive threads.
- return Runtime::Current()->InJankPerceptibleProcessState() && Thread::IsSensitiveThread();
+ return Runtime::Current()->InJankPerceptibleProcessState()
+ && Thread::Current()->IsJitSensitiveThread();
}
void Jit::DumpInfo(std::ostream& os) {
@@ -119,6 +131,11 @@
memory_use_.PrintMemoryUse(os);
}
+void Jit::DumpForSigQuit(std::ostream& os) {
+ DumpInfo(os);
+ ProfileSaver::DumpInstanceInfo(os);
+}
+
void Jit::AddTimingLogger(const TimingLogger& logger) {
cumulative_timings_.AddLogger(logger);
}
@@ -127,9 +144,11 @@
cumulative_timings_("JIT timings"),
memory_use_("Memory used for compilation", 16),
lock_("JIT memory use lock"),
+ use_jit_compilation_(true),
save_profiling_info_(false) {}
Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
+ DCHECK(options->UseJitCompilation() || options->GetSaveProfilingInfo());
std::unique_ptr<Jit> jit(new Jit);
jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
@@ -143,6 +162,7 @@
if (jit->GetCodeCache() == nullptr) {
return nullptr;
}
+ jit->use_jit_compilation_ = options->UseJitCompilation();
jit->save_profiling_info_ = options->GetSaveProfilingInfo();
VLOG(jit) << "JIT created with initial_capacity="
<< PrettySize(options->GetCodeCacheInitialCapacity())
@@ -155,8 +175,7 @@
jit->warm_method_threshold_ = options->GetWarmupThreshold();
jit->osr_method_threshold_ = options->GetOsrThreshold();
jit->priority_thread_weight_ = options->GetPriorityThreadWeight();
- jit->transition_weight_ = std::max(
- jit->warm_method_threshold_ / kDefaultTransitionRatio, static_cast<size_t>(1));
+ jit->invoke_transition_weight_ = options->GetInvokeTransitionWeight();
jit->CreateThreadPool();
@@ -222,6 +241,7 @@
}
bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) {
+ DCHECK(Runtime::Current()->UseJitCompilation());
DCHECK(!method->IsRuntimeMethod());
// Don't compile the method if it has breakpoints.
@@ -297,7 +317,7 @@
void Jit::StopProfileSaver() {
if (save_profiling_info_ && ProfileSaver::IsStarted()) {
- ProfileSaver::Stop();
+ ProfileSaver::Stop(dump_info_on_shutdown_);
}
}
@@ -326,8 +346,12 @@
}
void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) {
+ if (!Runtime::Current()->UseJitCompilation()) {
+ // No need to notify if we only use the JIT to save profiles.
+ return;
+ }
jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->generate_debug_info_) {
+ if (jit->generate_debug_info_) {
DCHECK(jit->jit_types_loaded_ != nullptr);
jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1);
}
@@ -562,7 +586,7 @@
return;
}
- if (method->IsClassInitializer() || method->IsNative()) {
+ if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) {
// We do not want to compile such methods.
return;
}
@@ -601,22 +625,24 @@
}
// Avoid jumping more than one state at a time.
new_count = std::min(new_count, hot_method_threshold_ - 1);
- } else if (starting_count < hot_method_threshold_) {
- if ((new_count >= hot_method_threshold_) &&
- !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
- DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
- }
- // Avoid jumping more than one state at a time.
- new_count = std::min(new_count, osr_method_threshold_ - 1);
- } else if (starting_count < osr_method_threshold_) {
- if (!with_backedges) {
- // If the samples don't contain any back edge, we don't increment the hotness.
- return;
- }
- if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) {
- DCHECK(thread_pool_ != nullptr);
- thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ } else if (use_jit_compilation_) {
+ if (starting_count < hot_method_threshold_) {
+ if ((new_count >= hot_method_threshold_) &&
+ !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
+ }
+ // Avoid jumping more than one state at a time.
+ new_count = std::min(new_count, osr_method_threshold_ - 1);
+ } else if (starting_count < osr_method_threshold_) {
+ if (!with_backedges) {
+ // If the samples don't contain any back edge, we don't increment the hotness.
+ return;
+ }
+ if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) {
+ DCHECK(thread_pool_ != nullptr);
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
+ }
}
}
// Update hotness counter
@@ -624,7 +650,8 @@
}
void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
- if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) {
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) {
// The compiler requires a ProfilingInfo object.
ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
JitCompileTask compile_task(method, JitCompileTask::kCompile);
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index ff3acf6..f3a6240 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -44,7 +44,7 @@
static constexpr bool kStressMode = kIsDebugBuild;
static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000;
static constexpr size_t kDefaultPriorityThreadWeightRatio = 1000;
- static constexpr size_t kDefaultTransitionRatio = 100;
+ static constexpr size_t kDefaultInvokeTransitionWeightRatio = 500;
virtual ~Jit();
static Jit* Create(JitOptions* options, std::string* error_msg);
@@ -87,6 +87,15 @@
return priority_thread_weight_;
}
+ // Returns false if we only need to save profile information and not compile methods.
+ bool UseJitCompilation() const {
+ return use_jit_compilation_;
+ }
+
+ bool SaveProfilingInfo() const {
+ return save_profiling_info_;
+ }
+
// Wait until there is no more pending compilation tasks.
void WaitForCompilationToFinish(Thread* self);
@@ -106,12 +115,12 @@
void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller)
SHARED_REQUIRES(Locks::mutator_lock_) {
- AddSamples(self, caller, transition_weight_, false);
+ AddSamples(self, caller, invoke_transition_weight_, false);
}
void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee)
SHARED_REQUIRES(Locks::mutator_lock_) {
- AddSamples(self, callee, transition_weight_, false);
+ AddSamples(self, callee, invoke_transition_weight_, false);
}
// Starts the profile saver if the config options allow profile recording.
@@ -127,9 +136,7 @@
const std::string& app_dir);
void StopProfileSaver();
- void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_) {
- DumpInfo(os);
- }
+ void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_);
static void NewTypeLoadedIfUsingJit(mirror::Class* type)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -181,13 +188,14 @@
std::unique_ptr<jit::JitCodeCache> code_cache_;
+ bool use_jit_compilation_;
bool save_profiling_info_;
static bool generate_debug_info_;
uint16_t hot_method_threshold_;
uint16_t warm_method_threshold_;
uint16_t osr_method_threshold_;
uint16_t priority_thread_weight_;
- uint16_t transition_weight_;
+ uint16_t invoke_transition_weight_;
std::unique_ptr<ThreadPool> thread_pool_;
DISALLOW_COPY_AND_ASSIGN(Jit);
@@ -208,6 +216,9 @@
uint16_t GetPriorityThreadWeight() const {
return priority_thread_weight_;
}
+ size_t GetInvokeTransitionWeight() const {
+ return invoke_transition_weight_;
+ }
size_t GetCodeCacheInitialCapacity() const {
return code_cache_initial_capacity_;
}
@@ -220,33 +231,34 @@
bool GetSaveProfilingInfo() const {
return save_profiling_info_;
}
- bool UseJIT() const {
- return use_jit_;
+ bool UseJitCompilation() const {
+ return use_jit_compilation_;
}
- void SetUseJIT(bool b) {
- use_jit_ = b;
+ void SetUseJitCompilation(bool b) {
+ use_jit_compilation_ = b;
}
void SetSaveProfilingInfo(bool b) {
save_profiling_info_ = b;
}
void SetJitAtFirstUse() {
- use_jit_ = true;
+ use_jit_compilation_ = true;
compile_threshold_ = 0;
}
private:
- bool use_jit_;
+ bool use_jit_compilation_;
size_t code_cache_initial_capacity_;
size_t code_cache_max_capacity_;
size_t compile_threshold_;
size_t warmup_threshold_;
size_t osr_threshold_;
uint16_t priority_thread_weight_;
+ size_t invoke_transition_weight_;
bool dump_info_on_shutdown_;
bool save_profiling_info_;
JitOptions()
- : use_jit_(false),
+ : use_jit_compilation_(false),
code_cache_initial_capacity_(0),
code_cache_max_capacity_(0),
compile_threshold_(0),
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 752d4ba..6b6f5a5 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -887,13 +887,15 @@
}
}
-void JitCodeCache::GetCompiledArtMethods(const std::set<std::string>& dex_base_locations,
- std::vector<ArtMethod*>& methods) {
+void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations,
+ std::vector<MethodReference>& methods) {
ScopedTrace trace(__FUNCTION__);
MutexLock mu(Thread::Current(), lock_);
- for (auto it : method_code_map_) {
- if (ContainsElement(dex_base_locations, it.second->GetDexFile()->GetBaseLocation())) {
- methods.push_back(it.second);
+ for (const ProfilingInfo* info : profiling_infos_) {
+ ArtMethod* method = info->GetMethod();
+ const DexFile* dex_file = method->GetDexFile();
+ if (ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
+ methods.emplace_back(dex_file, method->GetDexMethodIndex());
}
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index f31cc51..4df6762 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -26,6 +26,7 @@
#include "gc/accounting/bitmap.h"
#include "gc_root.h"
#include "jni.h"
+#include "method_reference.h"
#include "oat_file.h"
#include "object_callbacks.h"
#include "safe_map.h"
@@ -165,9 +166,9 @@
void* MoreCore(const void* mspace, intptr_t increment);
- // Adds to `methods` all the compiled ArtMethods which are part of any of the given dex locations.
- void GetCompiledArtMethods(const std::set<std::string>& dex_base_locations,
- std::vector<ArtMethod*>& methods)
+ // Adds to `methods` all profiled methods which are part of any of the given dex locations.
+ void GetProfiledMethods(const std::set<std::string>& dex_base_locations,
+ std::vector<MethodReference>& methods)
REQUIRES(!lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index f181ca3..c99d363 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -16,7 +16,8 @@
#include "offline_profiling_info.h"
-#include <fstream>
+#include "errno.h"
+#include <limits.h>
#include <vector>
#include <sys/file.h>
#include <sys/stat.h>
@@ -34,6 +35,11 @@
namespace art {
+const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '1', '\0' };
+
+static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
+
// Transform the actual dex location into relative paths.
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
@@ -49,15 +55,27 @@
}
}
-bool ProfileCompilationInfo::SaveProfilingInfo(
- const std::string& filename,
- const std::vector<ArtMethod*>& methods,
+bool ProfileCompilationInfo::AddMethodsAndClasses(
+ const std::vector<MethodReference>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
- if (methods.empty() && resolved_classes.empty()) {
- VLOG(profiler) << "No info to save to " << filename;
- return true;
+ for (const MethodReference& method : methods) {
+ if (!AddMethodIndex(GetProfileDexFileKey(method.dex_file->GetLocation()),
+ method.dex_file->GetLocationChecksum(),
+ method.dex_method_index)) {
+ return false;
+ }
}
+ for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
+ if (!AddResolvedClasses(dex_cache)) {
+ return false;
+ }
+ }
+ return true;
+}
+bool ProfileCompilationInfo::MergeAndSave(const std::string& filename,
+ uint64_t* bytes_written,
+ bool force) {
ScopedTrace trace(__PRETTY_FUNCTION__);
ScopedFlock flock;
std::string error;
@@ -68,26 +86,37 @@
int fd = flock.GetFile()->Fd();
- ProfileCompilationInfo info;
- if (!info.Load(fd)) {
- LOG(WARNING) << "Could not load previous profile data from file " << filename;
- return false;
- }
- {
- ScopedObjectAccess soa(Thread::Current());
- for (ArtMethod* method : methods) {
- const DexFile* dex_file = method->GetDexFile();
- if (!info.AddMethodIndex(GetProfileDexFileKey(dex_file->GetLocation()),
- dex_file->GetLocationChecksum(),
- method->GetDexMethodIndex())) {
+ // Load the file but keep a copy around to be able to infer if the content has changed.
+ ProfileCompilationInfo fileInfo;
+ ProfileLoadSatus status = fileInfo.LoadInternal(fd, &error);
+ if (status == kProfileLoadSuccess) {
+ // Merge the content of file into the current object.
+ if (MergeWith(fileInfo)) {
+ // If after the merge we have the same data as what is the file there's no point
+ // in actually doing the write. The file will be exactly the same as before.
+ if (Equals(fileInfo)) {
+ if (bytes_written != nullptr) {
+ *bytes_written = 0;
+ }
+ return true;
+ }
+ } else {
+ LOG(WARNING) << "Could not merge previous profile data from file " << filename;
+ if (!force) {
return false;
}
}
- for (const DexCacheResolvedClasses& dex_cache : resolved_classes) {
- info.AddResolvedClasses(dex_cache);
- }
+ } else if (force &&
+ ((status == kProfileLoadVersionMismatch) || (status == kProfileLoadBadData))) {
+ // Log a warning but don't return false. We will clear the profile anyway.
+ LOG(WARNING) << "Clearing bad or obsolete profile data from file "
+ << filename << ": " << error;
+ } else {
+ LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
+ return false;
}
+ // We need to clear the data because we don't support appending to the profiles yet.
if (!flock.GetFile()->ClearContent()) {
PLOG(WARNING) << "Could not clear profile file: " << filename;
return false;
@@ -95,95 +124,118 @@
// This doesn't need locking because we are trying to lock the file for exclusive
// access and fail immediately if we can't.
- bool result = info.Save(fd);
+ bool result = Save(fd);
if (result) {
VLOG(profiler) << "Successfully saved profile info to " << filename
<< " Size: " << GetFileSizeBytes(filename);
+ if (bytes_written != nullptr) {
+ *bytes_written = GetFileSizeBytes(filename);
+ }
} else {
VLOG(profiler) << "Failed to save profile info to " << filename;
}
return result;
}
-static bool WriteToFile(int fd, const std::ostringstream& os) {
- std::string data(os.str());
- const char *p = data.c_str();
- size_t length = data.length();
- do {
- int n = TEMP_FAILURE_RETRY(write(fd, p, length));
- if (n < 0) {
- PLOG(WARNING) << "Failed to write to descriptor: " << fd;
+// Returns true if all the bytes were successfully written to the file descriptor.
+static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
+ while (byte_count > 0) {
+ int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
+ if (bytes_written == -1) {
return false;
}
- p += n;
- length -= n;
- } while (length > 0);
+ byte_count -= bytes_written; // Reduce the number of remaining bytes.
+ buffer += bytes_written; // Move the buffer forward.
+ }
return true;
}
-static constexpr const char kFieldSeparator = ',';
-static constexpr const char kLineSeparator = '\n';
-static constexpr const char* kClassesMarker = "classes";
+// Add the string bytes to the buffer.
+static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
+ buffer->insert(buffer->end(), value.begin(), value.end());
+}
+
+// Insert each byte, from low to high into the buffer.
+template <typename T>
+static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
+ for (size_t i = 0; i < sizeof(T); i++) {
+ buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
+ }
+}
+
+static constexpr size_t kLineHeaderSize =
+ 3 * sizeof(uint16_t) + // method_set.size + class_set.size + dex_location.size
+ sizeof(uint32_t); // checksum
/**
* Serialization format:
- * dex_location1,dex_location_checksum1,method_id11,method_id12...,classes,class_id1,class_id2...
- * dex_location2,dex_location_checksum2,method_id21,method_id22...,classes,class_id1,class_id2...
- * e.g.
- * app.apk,131232145,11,23,454,54,classes,1,2,4,1234
- * app.apk:classes5.dex,218490184,39,13,49,1
+ * magic,version,number_of_lines
+ * dex_location1,number_of_methods1,number_of_classes1,dex_location_checksum1, \
+ * method_id11,method_id12...,class_id1,class_id2...
+ * dex_location2,number_of_methods2,number_of_classes2,dex_location_checksum2, \
+ * method_id21,method_id22...,,class_id1,class_id2...
+ * .....
**/
bool ProfileCompilationInfo::Save(int fd) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- // TODO(calin): Profile this and see how much memory it takes. If too much,
- // write to file directly.
- std::ostringstream os;
+
+ // Cache at most 5KB before writing.
+ static constexpr size_t kMaxSizeToKeepBeforeWriting = 5 * KB;
+ // Use a vector wrapper to avoid keeping track of offsets when we add elements.
+ std::vector<uint8_t> buffer;
+ WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
+ WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(info_.size()));
+
for (const auto& it : info_) {
+ if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
+ if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
+ return false;
+ }
+ buffer.clear();
+ }
const std::string& dex_location = it.first;
const DexFileData& dex_data = it.second;
if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
continue;
}
- os << dex_location << kFieldSeparator << dex_data.checksum;
+ if (dex_location.size() >= kMaxDexFileKeyLength) {
+ LOG(WARNING) << "DexFileKey exceeds allocated limit";
+ return false;
+ }
+
+ // Make sure that the buffer has enough capacity to avoid repeated resizings
+ // while we add data.
+ size_t required_capacity = buffer.size() +
+ kLineHeaderSize +
+ dex_location.size() +
+ sizeof(uint16_t) * (dex_data.class_set.size() + dex_data.method_set.size());
+
+ buffer.reserve(required_capacity);
+
+ DCHECK_LE(dex_location.size(), std::numeric_limits<uint16_t>::max());
+ DCHECK_LE(dex_data.method_set.size(), std::numeric_limits<uint16_t>::max());
+ DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_location.size()));
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.method_set.size()));
+ AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
+ AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
+
+ AddStringToBuffer(&buffer, dex_location);
+
for (auto method_it : dex_data.method_set) {
- os << kFieldSeparator << method_it;
+ AddUintToBuffer(&buffer, method_it);
}
- if (!dex_data.class_set.empty()) {
- os << kFieldSeparator << kClassesMarker;
- for (auto class_id : dex_data.class_set) {
- os << kFieldSeparator << class_id;
- }
+ for (auto class_id : dex_data.class_set) {
+ AddUintToBuffer(&buffer, class_id);
}
- os << kLineSeparator;
+ DCHECK_EQ(required_capacity, buffer.size())
+ << "Failed to add the expected number of bytes in the buffer";
}
- return WriteToFile(fd, os);
-}
-
-// TODO(calin): This a duplicate of Utils::Split fixing the case where the first character
-// is the separator. Merge the fix into Utils::Split once verified that it doesn't break its users.
-static void SplitString(const std::string& s, char separator, std::vector<std::string>* result) {
- const char* p = s.data();
- const char* end = p + s.size();
- // Check if the first character is the separator.
- if (p != end && *p ==separator) {
- result->push_back("");
- ++p;
- }
- // Process the rest of the characters.
- while (p != end) {
- if (*p == separator) {
- ++p;
- } else {
- const char* start = p;
- while (++p != end && *p != separator) {
- // Skip to the next occurrence of the separator.
- }
- result->push_back(std::string(start, p - start));
- }
- }
+ return WriteBuffer(fd, buffer.data(), buffer.size());
}
ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
@@ -233,120 +285,260 @@
return true;
}
-bool ProfileCompilationInfo::ProcessLine(const std::string& line) {
- std::vector<std::string> parts;
- SplitString(line, kFieldSeparator, &parts);
- if (parts.size() < 3) {
- LOG(WARNING) << "Invalid line: " << line;
- return false;
- }
-
- const std::string& dex_location = parts[0];
- uint32_t checksum;
- if (!ParseInt(parts[1].c_str(), &checksum)) {
- return false;
- }
-
- for (size_t i = 2; i < parts.size(); i++) {
- if (parts[i] == kClassesMarker) {
- ++i;
- // All of the remaining idx are class def indexes.
- for (++i; i < parts.size(); ++i) {
- uint32_t class_def_idx;
- if (!ParseInt(parts[i].c_str(), &class_def_idx)) {
- LOG(WARNING) << "Cannot parse class_def_idx " << parts[i];
- return false;
- } else if (class_def_idx >= std::numeric_limits<uint16_t>::max()) {
- LOG(WARNING) << "Class def idx " << class_def_idx << " is larger than uint16_t max";
- return false;
- }
- if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
- return false;
- }
- }
- break;
- }
- uint32_t method_idx;
- if (!ParseInt(parts[i].c_str(), &method_idx)) {
- LOG(WARNING) << "Cannot parse method_idx " << parts[i];
- return false;
- }
+bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer,
+ uint16_t method_set_size,
+ uint16_t class_set_size,
+ uint32_t checksum,
+ const std::string& dex_location) {
+ for (uint16_t i = 0; i < method_set_size; i++) {
+ uint16_t method_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
if (!AddMethodIndex(dex_location, checksum, method_idx)) {
return false;
}
}
+
+ for (uint16_t i = 0; i < class_set_size; i++) {
+ uint16_t class_def_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
+ if (!AddClassIndex(dex_location, checksum, class_def_idx)) {
+ return false;
+ }
+ }
return true;
}
-// Parses the buffer (of length n) starting from start_from and identify new lines
-// based on kLineSeparator marker.
-// Returns the first position after kLineSeparator in the buffer (starting from start_from),
-// or -1 if the marker doesn't appear.
-// The processed characters are appended to the given line.
-static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& line) {
- if (start_from >= n) {
- return -1;
+// Tests for EOF by trying to read 1 byte from the descriptor.
+// Returns:
+// 0 if the descriptor is at the EOF,
+// -1 if there was an IO error
+// 1 if the descriptor has more content to read
+static int testEOF(int fd) {
+ uint8_t buffer[1];
+ return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
+}
+
+// Reads an uint value previously written with AddUintToBuffer.
+template <typename T>
+T ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance() {
+ static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
+ CHECK_LE(ptr_current_ + sizeof(T), ptr_end_);
+ T value = 0;
+ for (size_t i = 0; i < sizeof(T); i++) {
+ value += ptr_current_[i] << (i * kBitsPerByte);
}
- int new_line_pos = -1;
- for (int i = start_from; i < n; i++) {
- if (buffer[i] == kLineSeparator) {
- new_line_pos = i;
- break;
+ ptr_current_ += sizeof(T);
+ return value;
+}
+
+bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
+ if (ptr_current_ + data_size > ptr_end_) {
+ return false;
+ }
+ if (memcmp(ptr_current_, data, data_size) == 0) {
+ ptr_current_ += data_size;
+ return true;
+ }
+ return false;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
+ int fd,
+ const std::string& source,
+ /*out*/std::string* error) {
+ size_t byte_count = ptr_end_ - ptr_current_;
+ uint8_t* buffer = ptr_current_;
+ while (byte_count > 0) {
+ int bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, byte_count));
+ if (bytes_read == 0) {
+ *error += "Profile EOF reached prematurely for " + source;
+ return kProfileLoadBadData;
+ } else if (bytes_read < 0) {
+ *error += "Profile IO error for " + source + strerror(errno);
+ return kProfileLoadIOError;
}
+ byte_count -= bytes_read;
+ buffer += bytes_read;
}
- int append_limit = new_line_pos == -1 ? n : new_line_pos;
- line.append(buffer + start_from, append_limit - start_from);
- // Jump over kLineSeparator and return the position of the next character.
- return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
+ return kProfileLoadSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
+ int fd,
+ /*out*/uint16_t* number_of_lines,
+ /*out*/std::string* error) {
+ // Read magic and version
+ const size_t kMagicVersionSize =
+ sizeof(kProfileMagic) +
+ sizeof(kProfileVersion) +
+ sizeof(uint16_t); // number of lines
+
+ SafeBuffer safe_buffer(kMagicVersionSize);
+
+ ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
+ *error = "Profile missing magic";
+ return kProfileLoadVersionMismatch;
+ }
+ if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
+ *error = "Profile version mismatch";
+ return kProfileLoadVersionMismatch;
+ }
+ *number_of_lines = safe_buffer.ReadUintAndAdvance<uint16_t>();
+ return kProfileLoadSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
+ int fd,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error) {
+ SafeBuffer header_buffer(kLineHeaderSize);
+ ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileHeader", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ uint16_t dex_location_size = header_buffer.ReadUintAndAdvance<uint16_t>();
+ line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
+ line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
+ line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>();
+
+ if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
+ *error = "DexFileKey has an invalid size: " +
+ std::to_string(static_cast<uint32_t>(dex_location_size));
+ return kProfileLoadBadData;
+ }
+
+ SafeBuffer location_buffer(dex_location_size);
+ status = location_buffer.FillFromFd(fd, "ReadProfileHeaderDexLocation", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ line_header->dex_location.assign(
+ reinterpret_cast<char*>(location_buffer.Get()), dex_location_size);
+ return kProfileLoadSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
+ int fd,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error) {
+ // Make sure that we don't try to read everything in memory (in case the profile if full).
+ // Split readings in chunks of at most 10kb.
+ static constexpr uint16_t kMaxNumberOfEntriesToRead = 5120;
+ uint16_t methods_left_to_read = line_header.method_set_size;
+ uint16_t classes_left_to_read = line_header.class_set_size;
+
+ while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) {
+ uint16_t methods_to_read = std::min(kMaxNumberOfEntriesToRead, methods_left_to_read);
+ uint16_t max_classes_to_read = kMaxNumberOfEntriesToRead - methods_to_read;
+ uint16_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read);
+
+ size_t line_size = sizeof(uint16_t) * (methods_to_read + classes_to_read);
+ SafeBuffer line_buffer(line_size);
+
+ ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ if (!ProcessLine(line_buffer,
+ methods_to_read,
+ classes_to_read,
+ line_header.checksum,
+ line_header.dex_location)) {
+ *error = "Error when reading profile file line";
+ return kProfileLoadBadData;
+ }
+ methods_left_to_read -= methods_to_read;
+ classes_left_to_read -= classes_to_read;
+ }
+ return kProfileLoadSuccess;
}
bool ProfileCompilationInfo::Load(int fd) {
+ std::string error;
+ ProfileLoadSatus status = LoadInternal(fd, &error);
+
+ if (status == kProfileLoadSuccess) {
+ return true;
+ } else {
+ PLOG(WARNING) << "Error when reading profile " << error;
+ return false;
+ }
+}
+
+ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
+ int fd, std::string* error) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- std::string current_line;
- const int kBufferSize = 1024;
- char buffer[kBufferSize];
-
- while (true) {
- int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize));
- if (n < 0) {
- PLOG(WARNING) << "Error when reading profile file";
- return false;
- } else if (n == 0) {
- break;
- }
- // Detect the new lines from the buffer. If we manage to complete a line,
- // process it. Otherwise append to the current line.
- int current_start_pos = 0;
- while (current_start_pos < n) {
- current_start_pos = GetLineFromBuffer(buffer, n, current_start_pos, current_line);
- if (current_start_pos == -1) {
- break;
- }
- if (!ProcessLine(current_line)) {
- return false;
- }
- // Reset the current line (we just processed it).
- current_line.clear();
- }
+ struct stat stat_buffer;
+ if (fstat(fd, &stat_buffer) != 0) {
+ return kProfileLoadIOError;
}
- return true;
+ // We allow empty profile files.
+ // Profiles may be created by ActivityManager or installd before we manage to
+ // process them in the runtime or profman.
+ if (stat_buffer.st_size == 0) {
+ return kProfileLoadSuccess;
+ }
+ // Read profile header: magic + version + number_of_lines.
+ uint16_t number_of_lines;
+ ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ while (number_of_lines > 0) {
+ ProfileLineHeader line_header;
+ // First, read the line header to get the amount of data we need to read.
+ status = ReadProfileLineHeader(fd, &line_header, error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+
+ // Now read the actual profile line.
+ status = ReadProfileLine(fd, line_header, error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
+ number_of_lines--;
+ }
+
+ // Check that we read everything and that profiles don't contain junk data.
+ int result = testEOF(fd);
+ if (result == 0) {
+ return kProfileLoadSuccess;
+ } else if (result < 0) {
+ return kProfileLoadIOError;
+ } else {
+ *error = "Unexpected content in the profile file";
+ return kProfileLoadBadData;
+ }
}
-bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
+ // First verify that all checksums match. This will avoid adding garbage to
+ // the current profile info.
+ // Note that the number of elements should be very small, so this should not
+ // be a performance issue.
+ for (const auto& other_it : other.info_) {
+ auto info_it = info_.find(other_it.first);
+ if ((info_it != info_.end()) && (info_it->second.checksum != other_it.second.checksum)) {
+ LOG(WARNING) << "Checksum mismatch for dex " << other_it.first;
+ return false;
+ }
+ }
+ // All checksums match. Import the data.
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
const DexFileData& other_dex_data = other_it.second;
-
auto info_it = info_.find(other_dex_location);
if (info_it == info_.end()) {
info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
}
- if (info_it->second.checksum != other_dex_data.checksum) {
- LOG(WARNING) << "Checksum mismatch for dex " << other_dex_location;
- return false;
- }
info_it->second.method_set.insert(other_dex_data.method_set.begin(),
other_dex_data.method_set.end());
info_it->second.class_set.insert(other_dex_data.class_set.begin(),
@@ -387,6 +579,14 @@
return total;
}
+uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
+ uint32_t total = 0;
+ for (const auto& it : info_) {
+ total += it.second.class_set.size();
+ }
+ return total;
+}
+
std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location) const {
std::ostringstream os;
@@ -408,19 +608,29 @@
std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
}
- for (const auto method_it : dex_data.method_set) {
- if (dex_files != nullptr) {
- const DexFile* dex_file = nullptr;
- for (size_t i = 0; i < dex_files->size(); i++) {
- if (location == (*dex_files)[i]->GetLocation()) {
- dex_file = (*dex_files)[i];
- }
- }
- if (dex_file != nullptr) {
- os << "\n " << PrettyMethod(method_it, *dex_file, true);
+ const DexFile* dex_file = nullptr;
+ if (dex_files != nullptr) {
+ for (size_t i = 0; i < dex_files->size(); i++) {
+ if (location == (*dex_files)[i]->GetLocation()) {
+ dex_file = (*dex_files)[i];
}
}
- os << "\n " << method_it;
+ }
+ os << "\n\tmethods: ";
+ for (const auto method_it : dex_data.method_set) {
+ if (dex_file != nullptr) {
+ os << "\n\t\t" << PrettyMethod(method_it, *dex_file, true);
+ } else {
+ os << method_it << ",";
+ }
+ }
+ os << "\n\tclasses: ";
+ for (const auto class_it : dex_data.class_set) {
+ if (dex_file != nullptr) {
+ os << "\n\t\t" << PrettyType(class_it, *dex_file);
+ } else {
+ os << class_it << ",";
+ }
}
}
return os.str();
@@ -435,11 +645,18 @@
for (auto&& pair : info_) {
const std::string& profile_key = pair.first;
const DexFileData& data = pair.second;
- DexCacheResolvedClasses classes(profile_key, data.checksum);
+ // TODO: Is it OK to use the same location for both base and dex location here?
+ DexCacheResolvedClasses classes(profile_key, profile_key, data.checksum);
classes.AddClasses(data.class_set.begin(), data.class_set.end());
ret.insert(classes);
}
return ret;
}
+void ProfileCompilationInfo::ClearResolvedClasses() {
+ for (auto& pair : info_) {
+ pair.second.class_set.clear();
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index df03244..5a07da7 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -28,9 +28,6 @@
namespace art {
-class ArtMethod;
-class DexCacheProfileData;
-
// TODO: rename file.
/**
* Profile information in a format suitable to be queried by the compiler and
@@ -41,21 +38,29 @@
*/
class ProfileCompilationInfo {
public:
- // Saves profile information about the given methods in the given file.
- // Note that the saving proceeds only if the file can be locked for exclusive access.
- // If not (the locking is not blocking), the function does not save and returns false.
- static bool SaveProfilingInfo(const std::string& filename,
- const std::vector<ArtMethod*>& methods,
- const std::set<DexCacheResolvedClasses>& resolved_classes);
+ static const uint8_t kProfileMagic[];
+ static const uint8_t kProfileVersion[];
+ // Add the given methods and classes to the current profile object.
+ bool AddMethodsAndClasses(const std::vector<MethodReference>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes);
// Loads profile information from the given file descriptor.
bool Load(int fd);
- // Loads the data from another ProfileCompilationInfo object.
- bool Load(const ProfileCompilationInfo& info);
+ // Merge the data from another ProfileCompilationInfo into the current object.
+ bool MergeWith(const ProfileCompilationInfo& info);
// Saves the profile data to the given file descriptor.
bool Save(int fd);
+ // Loads and merges profile information from the given file into the current
+ // object and tries to save it back to disk.
+ // If `force` is true then the save will go through even if the given file
+ // has bad data or its version does not match. In this cases the profile content
+ // is ignored.
+ bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force);
+
// Returns the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
+ // Returns the number of resolved classes that were profiled.
+ uint32_t GetNumberOfResolvedClasses() const;
// Returns true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
@@ -70,8 +75,8 @@
std::string DumpInfo(const std::vector<const DexFile*>* dex_files,
bool print_full_dex_location = true) const;
- // For testing purposes.
bool Equals(const ProfileCompilationInfo& other);
+
static std::string GetProfileDexFileKey(const std::string& dex_location);
// Returns the class descriptors for all of the classes in the profiles' class sets.
@@ -79,7 +84,17 @@
// profile info stuff to generate a map back to the dex location.
std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
+ // Clears the resolved classes from the current object.
+ void ClearResolvedClasses();
+
private:
+ enum ProfileLoadSatus {
+ kProfileLoadIOError,
+ kProfileLoadVersionMismatch,
+ kProfileLoadBadData,
+ kProfileLoadSuccess
+ };
+
struct DexFileData {
explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
uint32_t checksum;
@@ -96,9 +111,65 @@
DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum);
bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
bool AddClassIndex(const std::string& dex_location, uint32_t checksum, uint16_t class_idx);
- bool AddResolvedClasses(const DexCacheResolvedClasses& classes)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool ProcessLine(const std::string& line);
+ bool AddResolvedClasses(const DexCacheResolvedClasses& classes);
+
+ // Parsing functionality.
+
+ struct ProfileLineHeader {
+ std::string dex_location;
+ uint16_t method_set_size;
+ uint16_t class_set_size;
+ uint32_t checksum;
+ };
+
+ // A helper structure to make sure we don't read past our buffers in the loops.
+ struct SafeBuffer {
+ public:
+ explicit SafeBuffer(size_t size) : storage_(new uint8_t[size]) {
+ ptr_current_ = storage_.get();
+ ptr_end_ = ptr_current_ + size;
+ }
+
+ // Reads the content of the descriptor at the current position.
+ ProfileLoadSatus FillFromFd(int fd,
+ const std::string& source,
+ /*out*/std::string* error);
+
+ // Reads an uint value (high bits to low bits) and advances the current pointer
+ // with the number of bits read.
+ template <typename T> T ReadUintAndAdvance();
+
+ // Compares the given data with the content current pointer. If the contents are
+ // equal it advances the current pointer by data_size.
+ bool CompareAndAdvance(const uint8_t* data, size_t data_size);
+
+ // Get the underlying raw buffer.
+ uint8_t* Get() { return storage_.get(); }
+
+ private:
+ std::unique_ptr<uint8_t> storage_;
+ uint8_t* ptr_current_;
+ uint8_t* ptr_end_;
+ };
+
+ ProfileLoadSatus LoadInternal(int fd, std::string* error);
+
+ ProfileLoadSatus ReadProfileHeader(int fd,
+ /*out*/uint16_t* number_of_lines,
+ /*out*/std::string* error);
+
+ ProfileLoadSatus ReadProfileLineHeader(int fd,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error);
+ ProfileLoadSatus ReadProfileLine(int fd,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error);
+
+ bool ProcessLine(SafeBuffer& line_buffer,
+ uint16_t method_set_size,
+ uint16_t class_set_size,
+ uint32_t checksum,
+ const std::string& dex_location);
friend class ProfileCompilationInfoTest;
friend class CompilerDriverProfileTest;
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index fdd8c6e..c8f4d94 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -21,6 +21,7 @@
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "dex_file.h"
+#include "method_reference.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "handle_scope-inl.h"
@@ -49,16 +50,44 @@
return methods;
}
- bool AddData(const std::string& dex_location,
- uint32_t checksum,
- uint16_t method_index,
- ProfileCompilationInfo* info) {
+ bool AddMethod(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_index,
+ ProfileCompilationInfo* info) {
return info->AddMethodIndex(dex_location, checksum, method_index);
}
+ bool AddClass(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t class_index,
+ ProfileCompilationInfo* info) {
+ return info->AddMethodIndex(dex_location, checksum, class_index);
+ }
+
uint32_t GetFd(const ScratchFile& file) {
return static_cast<uint32_t>(file.GetFd());
}
+
+ bool SaveProfilingInfo(
+ const std::string& filename,
+ const std::vector<ArtMethod*>& methods,
+ const std::set<DexCacheResolvedClasses>& resolved_classes) {
+ ProfileCompilationInfo info;
+ std::vector<MethodReference> method_refs;
+ ScopedObjectAccess soa(Thread::Current());
+ for (ArtMethod* method : methods) {
+ method_refs.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
+ }
+ if (!info.AddMethodsAndClasses(method_refs, resolved_classes)) {
+ return false;
+ }
+ return info.MergeAndSave(filename, nullptr, false);
+ }
+
+ // Cannot sizeof the actual arrays so hardcode the values here.
+ // They should not change anyway.
+ static constexpr int kProfileMagicSize = 4;
+ static constexpr int kProfileVersionSize = 4;
};
TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
@@ -75,9 +104,7 @@
// Save virtual methods from Main.
std::set<DexCacheResolvedClasses> resolved_classes;
std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
- ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
- main_methods,
- resolved_classes));
+ ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes));
// Check that what we saved is in the profile.
ProfileCompilationInfo info1;
@@ -92,9 +119,7 @@
// Save virtual methods from Second.
std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
- ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
- second_methods,
- resolved_classes));
+ ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes));
// Check that what we saved is in the profile (methods form Main and Second).
ProfileCompilationInfo info2;
@@ -118,8 +143,8 @@
ProfileCompilationInfo saved_info;
// Save a few methods.
for (uint16_t i = 0; i < 10; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
ASSERT_EQ(0, profile.GetFile()->Flush());
@@ -132,9 +157,9 @@
// Save more methods.
for (uint16_t i = 0; i < 100; i++) {
- ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
- ASSERT_TRUE(AddData("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
}
ASSERT_TRUE(profile.GetFile()->ResetOffset());
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
@@ -147,25 +172,156 @@
ASSERT_TRUE(loaded_info2.Equals(saved_info));
}
-TEST_F(ProfileCompilationInfoTest, AddDataFail) {
+TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
ScratchFile profile;
ProfileCompilationInfo info;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
// Trying to add info for an existing file but with a different checksum.
- ASSERT_FALSE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
+ ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
}
-TEST_F(ProfileCompilationInfoTest, LoadFail) {
+TEST_F(ProfileCompilationInfoTest, MergeFail) {
ScratchFile profile;
ProfileCompilationInfo info1;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
// Use the same file, change the checksum.
ProfileCompilationInfo info2;
- ASSERT_TRUE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
+ ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
- ASSERT_FALSE(info1.Load(info2));
+ ASSERT_FALSE(info1.MergeWith(info2));
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ // Save the maximum number of methods
+ for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
+ }
+ // Save the maximum number of classes
+ for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
+ ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, /* class_idx */ i, &saved_info));
+ ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, /* class_idx */ i, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo empyt_info;
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Equals(empyt_info));
+}
+
+TEST_F(ProfileCompilationInfoTest, BadMagic) {
+ ScratchFile profile;
+ uint8_t buffer[] = { 1, 2, 3, 4 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, BadVersion) {
+ ScratchFile profile;
+
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
+ uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, Incomplete) {
+ ScratchFile profile;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
+ // Write that we have at least one line.
+ uint8_t line_number[] = { 0, 1 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
+ ScratchFile profile;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
+ ASSERT_TRUE(profile.GetFile()->WriteFully(
+ ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
+ // Write that we have at least one line.
+ uint8_t line_number[] = { 0, 1 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+
+ // dex_location_size, methods_size, classes_size, checksum.
+ // Dex location size is too big and should be rejected.
+ uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+}
+
+TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ // Save the maximum number of methods
+ for (uint16_t i = 0; i < 10; i++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
+ }
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+
+ uint8_t random_data[] = { 1, 2, 3};
+ ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
+
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we fail because of unexpected data at the end of the file.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
}
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 6fe17db..e8462a1 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -22,25 +22,25 @@
#include "art_method-inl.h"
#include "base/systrace.h"
-#include "scoped_thread_state_change.h"
+#include "base/time_utils.h"
+#include "compiler_filter.h"
#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+
namespace art {
-// An arbitrary value to throttle save requests. Set to 2s for now.
-static constexpr const uint64_t kMilisecondsToNano = 1000000;
-static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * kMilisecondsToNano;
-
// TODO: read the constants from ProfileOptions,
// Add a random delay each time we go to sleep so that we don't hammer the CPU
// with all profile savers running at the same time.
-static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000; // 20 seconds
-static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes
-static constexpr const uint64_t kSavePeriodMs = 10 * 1000; // 10 seconds
-static constexpr const uint64_t kInitialDelayMs = 2 * 1000; // 2 seconds
-static constexpr const double kBackoffCoef = 1.5;
+static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000; // 30 seconds
+static constexpr const uint64_t kMaxBackoffMs = 10 * 60 * 1000; // 10 minutes
+static constexpr const uint64_t kSavePeriodMs = 20 * 1000; // 20 seconds
+static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000; // 2 seconds
+static constexpr const double kBackoffCoef = 2.0;
-static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
@@ -52,13 +52,21 @@
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),
+ last_save_number_of_methods_(0),
+ last_save_number_of_classes_(0),
wait_lock_("ProfileSaver wait lock"),
- period_condition_("ProfileSaver period condition", wait_lock_) {
- AddTrackedLocations(output_filename, code_paths);
- app_data_dir_ = "";
+ period_condition_("ProfileSaver period condition", wait_lock_),
+ total_bytes_written_(0),
+ total_number_of_writes_(0),
+ total_number_of_code_cache_queries_(0),
+ total_number_of_skipped_writes_(0),
+ total_number_of_failed_writes_(0),
+ total_ms_of_sleep_(0),
+ total_ns_of_work_(0),
+ total_number_of_foreign_dex_marks_(0),
+ max_number_of_profile_entries_cached_(0) {
+ AddTrackedLocations(output_filename, app_data_dir, code_paths);
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
@@ -66,9 +74,9 @@
// 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());
+ app_data_dirs_.emplace(app_data_dir_real_path.get());
} else {
- LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_
+ 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";
}
}
@@ -80,14 +88,13 @@
uint64_t save_period_ms = kSavePeriodMs;
VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
-
- bool first_iteration = true;
+ bool cache_resolved_classes = true;
while (!ShuttingDown(self)) {
uint64_t sleep_time_ms;
- if (first_iteration) {
+ if (cache_resolved_classes) {
// Sleep less long for the first iteration since we want to record loaded classes shortly
// after app launch.
- sleep_time_ms = kInitialDelayMs;
+ sleep_time_ms = kSaveResolvedClassesDelayMs;
} else {
const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
sleep_time_ms = save_period_ms + random_sleep_delay_ms;
@@ -96,86 +103,166 @@
MutexLock mu(self, wait_lock_);
period_condition_.TimedWait(self, sleep_time_ms, 0);
}
-
+ total_ms_of_sleep_ += sleep_time_ms;
if (ShuttingDown(self)) {
break;
}
- if (!ProcessProfilingInfo() && save_period_ms < kMaxBackoffMs) {
- // If we don't need to save now it is less likely that we will need to do
- // so in the future. Increase the time between saves according to the
- // kBackoffCoef, but make it no larger than kMaxBackoffMs.
- save_period_ms = static_cast<uint64_t>(kBackoffCoef * save_period_ms);
+ uint64_t start = NanoTime();
+ if (cache_resolved_classes) {
+ // TODO(calin) This only considers the case of the primary profile file.
+ // Anything that gets loaded in the same VM will not have their resolved
+ // classes save (unless they started before the initial saving was done).
+ FetchAndCacheResolvedClasses();
} else {
- // Reset the period to the initial value as it's highly likely to JIT again.
- save_period_ms = kSavePeriodMs;
+ bool profile_saved_to_disk = ProcessProfilingInfo();
+ if (profile_saved_to_disk) {
+ // Reset the period to the initial value as it's highly likely to JIT again.
+ save_period_ms = kSavePeriodMs;
+ VLOG(profiler) << "Profile saver: saved something, period reset to: " << save_period_ms;
+ } else {
+ // If we don't need to save now it is less likely that we will need to do
+ // so in the future. Increase the time between saves according to the
+ // kBackoffCoef, but make it no larger than kMaxBackoffMs.
+ save_period_ms = std::min(kMaxBackoffMs,
+ static_cast<uint64_t>(kBackoffCoef * save_period_ms));
+ VLOG(profiler) << "Profile saver: nothing to save, delaying period to: " << save_period_ms;
+ }
}
- first_iteration = false;
+ cache_resolved_classes = false;
+
+ total_ns_of_work_ += (NanoTime() - start);
}
}
+ProfileCompilationInfo* ProfileSaver::GetCachedProfiledInfo(const std::string& filename) {
+ auto info_it = profile_cache_.find(filename);
+ if (info_it == profile_cache_.end()) {
+ info_it = profile_cache_.Put(filename, ProfileCompilationInfo());
+ }
+ return &info_it->second;
+}
+
+void ProfileSaver::FetchAndCacheResolvedClasses() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ std::set<DexCacheResolvedClasses> resolved_classes =
+ class_linker->GetResolvedClasses(/*ignore boot classes*/ true);
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ uint64_t total_number_of_profile_entries_cached = 0;
+
+ for (const auto& it : tracked_dex_base_locations_) {
+ std::set<DexCacheResolvedClasses> resolved_classes_for_location;
+ const std::string& filename = it.first;
+ const std::set<std::string>& locations = it.second;
+
+ for (const DexCacheResolvedClasses& classes : resolved_classes) {
+ if (locations.find(classes.GetBaseLocation()) != locations.end()) {
+ VLOG(profiler) << "Added classes for location " << classes.GetBaseLocation()
+ << " (" << classes.GetDexLocation() << ")";
+ resolved_classes_for_location.insert(classes);
+ } else {
+ VLOG(profiler) << "Location not found " << classes.GetBaseLocation()
+ << " (" << classes.GetDexLocation() << ")";
+ }
+ }
+ ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
+ info->AddMethodsAndClasses(std::vector<MethodReference>(), resolved_classes_for_location);
+ total_number_of_profile_entries_cached += resolved_classes_for_location.size();
+ }
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+}
+
bool ProfileSaver::ProcessProfilingInfo() {
ScopedTrace trace(__PRETTY_FUNCTION__);
- uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs();
- if (!first_profile_ && last_update_time_ns - code_cache_last_update_time_ns_
- < kMinimumTimeBetweenCodeCacheUpdatesNs) {
- VLOG(profiler) << "Not enough time has passed since the last code cache update."
- << "Last update: " << last_update_time_ns
- << " Last save: " << code_cache_last_update_time_ns_;
- return false;
- }
-
- uint64_t start = NanoTime();
- code_cache_last_update_time_ns_ = last_update_time_ns;
SafeMap<std::string, std::set<std::string>> tracked_locations;
{
// Make a copy so that we don't hold the lock while doing I/O.
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
tracked_locations = tracked_dex_base_locations_;
}
+
+ bool profile_file_saved = false;
+ uint64_t total_number_of_profile_entries_cached = 0;
for (const auto& it : tracked_locations) {
if (ShuttingDown(Thread::Current())) {
return true;
}
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
- std::vector<ArtMethod*> methods;
+ std::vector<MethodReference> methods;
{
ScopedObjectAccess soa(Thread::Current());
- jit_code_cache_->GetCompiledArtMethods(locations, methods);
+ jit_code_cache_->GetProfiledMethods(locations, methods);
+ total_number_of_code_cache_queries_++;
}
- // Always save for the first one for loaded classes profile.
- if (methods.size() < kMinimumNrOrMethodsToSave && !first_profile_) {
+
+ ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
+ cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
+ int64_t delta_number_of_methods =
+ cached_info->GetNumberOfMethods() -
+ static_cast<int64_t>(last_save_number_of_methods_);
+ int64_t delta_number_of_classes =
+ cached_info->GetNumberOfResolvedClasses() -
+ static_cast<int64_t>(last_save_number_of_classes_);
+
+ if (delta_number_of_methods < kMinimumNumberOfMethodsToSave &&
+ delta_number_of_classes < kMinimumNumberOfClassesToSave) {
VLOG(profiler) << "Not enough information to save to: " << filename
- <<" Nr of methods: " << methods.size();
- return false;
+ << " Nr of methods: " << delta_number_of_methods
+ << " Nr of classes: " << delta_number_of_classes;
+ total_number_of_skipped_writes_++;
+ continue;
}
-
- std::set<DexCacheResolvedClasses> resolved_classes;
- if (first_profile_) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- resolved_classes = class_linker->GetResolvedClasses(/*ignore boot classes*/true);
- }
-
- if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods, resolved_classes)) {
+ uint64_t bytes_written;
+ // Force the save. In case the profile data is corrupted or the the profile
+ // has the wrong version this will "fix" the file to the correct format.
+ if (cached_info->MergeAndSave(filename, &bytes_written, /*force*/ true)) {
+ last_save_number_of_methods_ = cached_info->GetNumberOfMethods();
+ last_save_number_of_classes_ = cached_info->GetNumberOfResolvedClasses();
+ // Clear resolved classes. No need to store them around as
+ // they don't change after the first write.
+ cached_info->ClearResolvedClasses();
+ if (bytes_written > 0) {
+ total_number_of_writes_++;
+ total_bytes_written_ += bytes_written;
+ profile_file_saved = true;
+ } else {
+ // At this point we could still have avoided the write.
+ // We load and merge the data from the file lazily at its first ever
+ // save attempt. So, whatever we are trying to save could already be
+ // in the file.
+ total_number_of_skipped_writes_++;
+ }
+ } else {
LOG(WARNING) << "Could not save profiling info to " << filename;
- return false;
+ total_number_of_failed_writes_++;
}
-
- VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start);
+ total_number_of_profile_entries_cached +=
+ cached_info->GetNumberOfMethods() +
+ cached_info->GetNumberOfResolvedClasses();
}
- first_profile_ = false;
- return true;
+ max_number_of_profile_entries_cached_ = std::max(
+ max_number_of_profile_entries_cached_,
+ total_number_of_profile_entries_cached);
+ return profile_file_saved;
}
void* ProfileSaver::RunProfileSaverThread(void* arg) {
Runtime* runtime = Runtime::Current();
- ProfileSaver* profile_saver = reinterpret_cast<ProfileSaver*>(arg);
- CHECK(runtime->AttachCurrentThread("Profile Saver",
- /*as_daemon*/true,
- runtime->GetSystemThreadGroup(),
- /*create_peer*/true));
+ bool attached = runtime->AttachCurrentThread("Profile Saver",
+ /*as_daemon*/true,
+ runtime->GetSystemThreadGroup(),
+ /*create_peer*/true);
+ if (!attached) {
+ CHECK(runtime->IsShuttingDown(Thread::Current()));
+ return nullptr;
+ }
+
+ ProfileSaver* profile_saver = reinterpret_cast<ProfileSaver*>(arg);
profile_saver->Run();
runtime->DetachCurrentThread();
@@ -183,15 +270,47 @@
return nullptr;
}
+static bool ShouldProfileLocation(const std::string& location) {
+ OatFileManager& oat_manager = Runtime::Current()->GetOatFileManager();
+ const OatFile* oat_file = oat_manager.FindOpenedOatFileFromDexLocation(location);
+ if (oat_file == nullptr) {
+ // This can happen if we fallback to run code directly from the APK.
+ // Profile it with the hope that the background dexopt will get us back into
+ // a good state.
+ VLOG(profiler) << "Asked to profile a location without an oat file:" << location;
+ return true;
+ }
+ CompilerFilter::Filter filter = oat_file->GetCompilerFilter();
+ if ((filter == CompilerFilter::kSpeed) || (filter == CompilerFilter::kEverything)) {
+ VLOG(profiler)
+ << "Skip profiling oat file because it's already speed|everything compiled: "
+ << location << " oat location: " << oat_file->GetLocation();
+ return false;
+ }
+ return true;
+}
+
void ProfileSaver::Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
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(Runtime::Current()->SaveProfileInfo());
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
+ std::vector<std::string> code_paths_to_profile;
+
+ for (const std::string& location : code_paths) {
+ if (ShouldProfileLocation(location)) {
+ code_paths_to_profile.push_back(location);
+ }
+ }
+ if (code_paths_to_profile.empty()) {
+ VLOG(profiler) << "No code paths should be profiled.";
+ return;
+ }
+
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
if (instance_ != nullptr) {
// If we already have an instance, make sure it uses the same jit_code_cache.
@@ -199,16 +318,16 @@
// apps which share the same runtime).
DCHECK_EQ(instance_->jit_code_cache_, jit_code_cache);
// Add the code_paths to the tracked locations.
- instance_->AddTrackedLocations(output_filename, code_paths);
+ instance_->AddTrackedLocations(output_filename, app_data_dir, code_paths_to_profile);
return;
}
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
- << ". Tracking: " << Join(code_paths, ':');
+ << ". Tracking: " << Join(code_paths_to_profile, ':');
instance_ = new ProfileSaver(output_filename,
jit_code_cache,
- code_paths,
+ code_paths_to_profile,
foreign_dex_profile_path,
app_data_dir);
@@ -219,7 +338,7 @@
"Profile saver thread");
}
-void ProfileSaver::Stop() {
+void ProfileSaver::Stop(bool dump_info) {
ProfileSaver* profile_saver = nullptr;
pthread_t profiler_pthread = 0U;
@@ -237,6 +356,9 @@
return;
}
instance_->shutting_down_ = true;
+ if (dump_info) {
+ instance_->DumpInfo(LOG(INFO));
+ }
}
{
@@ -267,49 +389,62 @@
}
void ProfileSaver::AddTrackedLocations(const std::string& output_filename,
+ const std::string& app_data_dir,
const std::vector<std::string>& code_paths) {
auto it = tracked_dex_base_locations_.find(output_filename);
if (it == tracked_dex_base_locations_.end()) {
tracked_dex_base_locations_.Put(output_filename,
std::set<std::string>(code_paths.begin(), code_paths.end()));
+ app_data_dirs_.insert(app_data_dir);
} else {
it->second.insert(code_paths.begin(), code_paths.end());
}
}
void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
+ if (!ShouldProfileLocation(dex_location)) {
+ return;
+ }
std::set<std::string> app_code_paths;
std::string foreign_dex_profile_path;
- std::string app_data_dir;
+ std::set<std::string> app_data_dirs;
{
MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
- DCHECK(instance_ != nullptr);
+ if (instance_ == nullptr) {
+ return;
+ }
// 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_;
+ app_data_dirs.insert(instance_->app_data_dirs_.begin(), instance_->app_data_dirs_.end());
}
- MaybeRecordDexUseInternal(dex_location,
- app_code_paths,
- foreign_dex_profile_path,
- app_data_dir);
+ bool mark_created = MaybeRecordDexUseInternal(dex_location,
+ app_code_paths,
+ foreign_dex_profile_path,
+ app_data_dirs);
+ if (mark_created) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ instance_->total_number_of_foreign_dex_marks_++;
+ }
+ }
}
-void ProfileSaver::MaybeRecordDexUseInternal(
+bool 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) {
+ const std::set<std::string>& app_data_dirs) {
if (dex_location.empty()) {
LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
- return;
+ return false;
}
if (foreign_dex_profile_path.empty()) {
LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
- return;
+ return false;
}
UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
@@ -320,14 +455,14 @@
? 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) {
+ if (app_data_dirs.find(dex_location_real_path_str) != app_data_dirs.end()) {
// The dex location is under the application folder. Nothing to record.
- return;
+ return false;
}
if (app_code_paths.find(dex_location) != app_code_paths.end()) {
// The dex location belongs to the application code paths. Nothing to record.
- return;
+ return false;
}
// 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
@@ -344,7 +479,7 @@
: 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;
+ return false;
}
}
@@ -362,12 +497,65 @@
if (close(fd) != 0) {
PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
}
+ return true;
} else {
if (errno != EEXIST) {
// Another app could have already created the file.
PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+ return false;
+ }
+ return true;
+ }
+}
+
+void ProfileSaver::DumpInstanceInfo(std::ostream& os) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ instance_->DumpInfo(os);
+ }
+}
+
+void ProfileSaver::DumpInfo(std::ostream& os) {
+ os << "ProfileSaver total_bytes_written=" << total_bytes_written_ << '\n'
+ << "ProfileSaver total_number_of_writes=" << total_number_of_writes_ << '\n'
+ << "ProfileSaver total_number_of_code_cache_queries="
+ << total_number_of_code_cache_queries_ << '\n'
+ << "ProfileSaver total_number_of_skipped_writes=" << total_number_of_skipped_writes_ << '\n'
+ << "ProfileSaver total_number_of_failed_writes=" << total_number_of_failed_writes_ << '\n'
+ << "ProfileSaver total_ms_of_sleep=" << total_ms_of_sleep_ << '\n'
+ << "ProfileSaver total_ms_of_work=" << NsToMs(total_ns_of_work_) << '\n'
+ << "ProfileSaver total_number_of_foreign_dex_marks="
+ << total_number_of_foreign_dex_marks_ << '\n'
+ << "ProfileSaver max_number_profile_entries_cached="
+ << max_number_of_profile_entries_cached_ << '\n';
+}
+
+
+void ProfileSaver::ForceProcessProfiles() {
+ ProfileSaver* saver = nullptr;
+ {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ saver = instance_;
+ }
+ // TODO(calin): this is not actually thread safe as the instance_ may have been deleted,
+ // but we only use this in testing when we now this won't happen.
+ // Refactor the way we handle the instance so that we don't end up in this situation.
+ if (saver != nullptr) {
+ saver->ProcessProfilingInfo();
+ }
+}
+
+bool ProfileSaver::HasSeenMethod(const std::string& profile,
+ const DexFile* dex_file,
+ uint16_t method_idx) {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ if (instance_ != nullptr) {
+ ProfileCompilationInfo* info = instance_->GetCachedProfiledInfo(profile);
+ if (info != nullptr) {
+ return info->ContainsMethod(MethodReference(dex_file, method_idx));
}
}
+ return false;
}
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index e7eab95..4f3cdc2 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -37,7 +37,7 @@
// Stops the profile saver thread.
// NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
- static void Stop()
+ static void Stop(bool dump_info_)
REQUIRES(!Locks::profiler_lock_, !wait_lock_)
NO_THREAD_SAFETY_ANALYSIS;
@@ -46,6 +46,15 @@
static void NotifyDexUse(const std::string& dex_location);
+ // If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing.
+ static void DumpInstanceInfo(std::ostream& os);
+
+ // Just for testing purpose.
+ static void ForceProcessProfiles();
+ static bool HasSeenMethod(const std::string& profile,
+ const DexFile* dex_file,
+ uint16_t method_idx);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -62,19 +71,33 @@
void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_);
// Processes the existing profiling info from the jit code cache and returns
// true if it needed to be saved to disk.
- bool ProcessProfilingInfo();
+ bool ProcessProfilingInfo()
+ REQUIRES(!Locks::profiler_lock_)
+ REQUIRES(!Locks::mutator_lock_);
+
// Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
void AddTrackedLocations(const std::string& output_filename,
+ const std::string& app_data_dir,
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
- static void MaybeRecordDexUseInternal(
+ // Retrieves the cached profile compilation info for the given profile file.
+ // If no entry exists, a new empty one will be created, added to the cache and
+ // then returned.
+ ProfileCompilationInfo* GetCachedProfiledInfo(const std::string& filename);
+ // Fetches the current resolved classes from the ClassLinker and stores them
+ // in the profile_cache_ for later save.
+ void FetchAndCacheResolvedClasses();
+
+ static bool 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);
+ const std::set<std::string>& app_data_dirs);
+
+ void DumpInfo(std::ostream& os);
// The only instance of the saver.
static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
@@ -82,18 +105,44 @@
static pthread_t profiler_pthread_ GUARDED_BY(Locks::profiler_lock_);
jit::JitCodeCache* jit_code_cache_;
+
+ // Collection of code paths that the profiles tracks.
+ // It maps profile locations to code paths (dex base locations).
SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_
GUARDED_BY(Locks::profiler_lock_);
+ // The directory were the we should store the code paths.
std::string foreign_dex_profile_path_;
- std::string app_data_dir_;
- uint64_t code_cache_last_update_time_ns_;
+
+ // A list of application directories, used to infer if a loaded dex belongs
+ // to the application or not. Multiple application data directories are possible when
+ // different apps share the same runtime.
+ std::set<std::string> app_data_dirs_ GUARDED_BY(Locks::profiler_lock_);
+
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
- bool first_profile_ = true;
+ uint32_t last_save_number_of_methods_;
+ uint32_t last_save_number_of_classes_;
+
+ // A local cache for the profile information. Maps each tracked file to its
+ // profile information. The size of this cache is usually very small and tops
+ // to just a few hundreds entries in the ProfileCompilationInfo objects.
+ // It helps avoiding unnecessary writes to disk.
+ SafeMap<std::string, ProfileCompilationInfo> profile_cache_;
// Save period condition support.
Mutex wait_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ConditionVariable period_condition_ GUARDED_BY(wait_lock_);
+ uint64_t total_bytes_written_;
+ uint64_t total_number_of_writes_;
+ uint64_t total_number_of_code_cache_queries_;
+ uint64_t total_number_of_skipped_writes_;
+ uint64_t total_number_of_failed_writes_;
+ uint64_t total_ms_of_sleep_;
+ uint64_t total_ns_of_work_;
+ uint64_t total_number_of_foreign_dex_marks_;
+ // TODO(calin): replace with an actual size.
+ uint64_t max_number_of_profile_entries_cached_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
};
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index c31b22e..fd7a125 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -60,7 +60,13 @@
// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
// if any particular method needs to be a default conflict. Used to figure out at runtime if
// invoking this method will throw an exception.
-static constexpr uint32_t kAccDefaultConflict = 0x00800000; // method (runtime)
+static constexpr uint32_t kAccDefaultConflict = 0x00800000; // method (runtime)
+
+// Set by the verifier for a method we do not want the compiler to compile.
+static constexpr uint32_t kAccCompileDontBother = 0x01000000; // method (runtime)
+
+// Set by the verifier for a method that could not be verified to follow structured locking.
+static constexpr uint32_t kAccMustCountLocks = 0x02000000; // method (runtime)
// Special runtime-only flags.
// Interface and all its super-interfaces with default methods have been recursively initialized.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 3680c78..f4bc222 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -215,6 +215,85 @@
obj_ = GcRoot<mirror::Object>(object);
}
+// Note: Adapted from CurrentMethodVisitor in thread.cc. We must not resolve here.
+
+struct NthCallerWithDexPcVisitor FINAL : public StackVisitor {
+ explicit NthCallerWithDexPcVisitor(Thread* thread, size_t frame)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFramesNoResolve),
+ method_(nullptr),
+ dex_pc_(0),
+ current_frame_number_(0),
+ wanted_frame_number_(frame) {}
+ bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+ if (m == nullptr || m->IsRuntimeMethod()) {
+ // Runtime method, upcall, or resolution issue. Skip.
+ return true;
+ }
+
+ // Is this the requested frame?
+ if (current_frame_number_ == wanted_frame_number_) {
+ method_ = m;
+ dex_pc_ = GetDexPc(false /* abort_on_error*/);
+ return false;
+ }
+
+ // Look for more.
+ current_frame_number_++;
+ return true;
+ }
+
+ ArtMethod* method_;
+ uint32_t dex_pc_;
+
+ private:
+ size_t current_frame_number_;
+ const size_t wanted_frame_number_;
+};
+
+// This function is inlined and just helps to not have the VLOG and ATRACE check at all the
+// potential tracing points.
+void Monitor::AtraceMonitorLock(Thread* self, mirror::Object* obj, bool is_wait) {
+ if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging) && ATRACE_ENABLED())) {
+ AtraceMonitorLockImpl(self, obj, is_wait);
+ }
+}
+
+void Monitor::AtraceMonitorLockImpl(Thread* self, mirror::Object* obj, bool is_wait) {
+ // Wait() requires a deeper call stack to be useful. Otherwise you'll see "Waiting at
+ // Object.java". Assume that we'll wait a nontrivial amount, so it's OK to do a longer
+ // stack walk than if !is_wait.
+ NthCallerWithDexPcVisitor visitor(self, is_wait ? 1U : 0U);
+ visitor.WalkStack(false);
+ const char* prefix = is_wait ? "Waiting on " : "Locking ";
+
+ const char* filename;
+ int32_t line_number;
+ TranslateLocation(visitor.method_, visitor.dex_pc_, &filename, &line_number);
+
+ // It would be nice to have a stable "ID" for the object here. However, the only stable thing
+ // would be the identity hashcode. But we cannot use IdentityHashcode here: For one, there are
+ // times when it is unsafe to make that call (see stack dumping for an explanation). More
+ // importantly, we would have to give up on thin-locking when adding systrace locks, as the
+ // identity hashcode is stored in the lockword normally (so can't be used with thin-locks).
+ //
+ // Because of thin-locks we also cannot use the monitor id (as there is no monitor). Monitor ids
+ // also do not have to be stable, as the monitor may be deflated.
+ std::string tmp = StringPrintf("%s %d at %s:%d",
+ prefix,
+ (obj == nullptr ? -1 : static_cast<int32_t>(reinterpret_cast<uintptr_t>(obj))),
+ (filename != nullptr ? filename : "null"),
+ line_number);
+ ATRACE_BEGIN(tmp.c_str());
+}
+
+void Monitor::AtraceMonitorUnlock() {
+ if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
+ ATRACE_END();
+ }
+}
+
std::string Monitor::PrettyContentionInfo(const std::string& owner_name,
pid_t owner_tid,
ArtMethod* owners_method,
@@ -228,8 +307,8 @@
std::ostringstream oss;
oss << "monitor contention with owner " << owner_name << " (" << owner_tid << ")";
if (owners_method != nullptr) {
- oss << " owner method=" << PrettyMethod(owners_method);
- oss << " from " << owners_filename << ":" << owners_line_number;
+ oss << " at " << PrettyMethod(owners_method);
+ oss << "(" << owners_filename << ":" << owners_line_number << ")";
}
oss << " waiters=" << num_waiters;
return oss.str();
@@ -246,10 +325,10 @@
if (lock_profiling_threshold_ != 0) {
locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
}
- return;
+ break;
} else if (owner_ == self) { // Recursive.
lock_count_++;
- return;
+ break;
}
// Contended.
const bool log_contention = (lock_profiling_threshold_ != 0);
@@ -284,8 +363,9 @@
const char* filename;
int32_t line_number;
TranslateLocation(m, pc, &filename, &line_number);
- oss << " blocking from " << (filename != nullptr ? filename : "null")
- << ":" << line_number;
+ oss << " blocking from "
+ << PrettyMethod(m) << "(" << (filename != nullptr ? filename : "null") << ":"
+ << line_number << ")";
ATRACE_BEGIN(oss.str().c_str());
}
monitor_contenders_.Wait(self); // Still contended so wait.
@@ -318,6 +398,8 @@
}
if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
if (wait_ms > kLongWaitMs && owners_method != nullptr) {
+ uint32_t pc;
+ ArtMethod* m = self->GetCurrentMethod(&pc);
// TODO: We should maybe check that original_owner is still a live thread.
LOG(WARNING) << "Long "
<< PrettyContentionInfo(original_owner_name,
@@ -325,7 +407,7 @@
owners_method,
owners_dex_pc,
num_waiters)
- << " for " << PrettyDuration(MsToNs(wait_ms));
+ << " in " << PrettyMethod(m) << " for " << PrettyDuration(MsToNs(wait_ms));
}
const char* owners_filename;
int32_t owners_line_number;
@@ -348,6 +430,8 @@
monitor_lock_.Lock(self); // Reacquire locks in order.
--num_waiters_;
}
+
+ AtraceMonitorLock(self, GetObject(), false /* is_wait */);
}
static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...)
@@ -457,6 +541,7 @@
}
if (owner == self) {
// We own the monitor, so nobody else can be in here.
+ AtraceMonitorUnlock();
if (lock_count_ == 0) {
owner_ = nullptr;
locking_method_ = nullptr;
@@ -523,6 +608,11 @@
uintptr_t saved_dex_pc = locking_dex_pc_;
locking_dex_pc_ = 0;
+ AtraceMonitorUnlock(); // For the implict Unlock() just above. This will only end the deepest
+ // nesting, but that is enough for the visualization, and corresponds to
+ // the single Lock() we do afterwards.
+ AtraceMonitorLock(self, GetObject(), true /* is_wait */);
+
bool was_interrupted = false;
{
// Update thread state. If the GC wakes up, it'll ignore us, knowing
@@ -586,6 +676,8 @@
self->ThrowNewException("Ljava/lang/InterruptedException;", nullptr);
}
+ AtraceMonitorUnlock(); // End Wait().
+
// Re-acquire the monitor and lock.
Lock(self);
monitor_lock_.Lock(self);
@@ -775,6 +867,7 @@
case LockWord::kUnlocked: {
LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0, lock_word.ReadBarrierState()));
if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, thin_locked)) {
+ AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */);
// CasLockWord enforces more than the acquire ordering we need here.
return h_obj.Get(); // Success!
}
@@ -790,10 +883,12 @@
lock_word.ReadBarrierState()));
if (!kUseReadBarrier) {
h_obj->SetLockWord(thin_locked, true);
+ AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */);
return h_obj.Get(); // Success!
} else {
// Use CAS to preserve the read barrier state.
if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, thin_locked)) {
+ AtraceMonitorLock(self, h_obj.Get(), false /* is_wait */);
return h_obj.Get(); // Success!
}
}
@@ -830,7 +925,7 @@
continue; // Start from the beginning.
default: {
LOG(FATAL) << "Invalid monitor state " << lock_word.GetState();
- return h_obj.Get();
+ UNREACHABLE();
}
}
}
@@ -869,11 +964,17 @@
if (!kUseReadBarrier) {
DCHECK_EQ(new_lw.ReadBarrierState(), 0U);
h_obj->SetLockWord(new_lw, true);
+ if (ATRACE_ENABLED()) {
+ ATRACE_END();
+ }
// Success!
return true;
} else {
// Use CAS to preserve the read barrier state.
if (h_obj->CasLockWordWeakSequentiallyConsistent(lock_word, new_lw)) {
+ if (ATRACE_ENABLED()) {
+ ATRACE_END();
+ }
// Success!
return true;
}
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 8c7496b..7b4b8f9 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -250,6 +250,17 @@
uint32_t GetOwnerThreadId() REQUIRES(!monitor_lock_);
+ // Support for systrace output of monitor operations.
+ ALWAYS_INLINE static void AtraceMonitorLock(Thread* self,
+ mirror::Object* obj,
+ bool is_wait)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ static void AtraceMonitorLockImpl(Thread* self,
+ mirror::Object* obj,
+ bool is_wait)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ ALWAYS_INLINE static void AtraceMonitorUnlock();
+
static uint32_t lock_profiling_threshold_;
Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc
index ce38e4f..a47a4b2 100644
--- a/runtime/monitor_pool.cc
+++ b/runtime/monitor_pool.cc
@@ -28,7 +28,11 @@
} // namespace mirror
MonitorPool::MonitorPool()
- : num_chunks_(0), capacity_(0), first_free_(nullptr) {
+ : current_chunk_list_index_(0), num_chunks_(0), current_chunk_list_capacity_(0),
+ first_free_(nullptr) {
+ for (size_t i = 0; i < kMaxChunkLists; ++i) {
+ monitor_chunks_[i] = nullptr; // Not absolutely required, but ...
+ }
AllocateChunk(); // Get our first chunk.
}
@@ -37,24 +41,19 @@
void MonitorPool::AllocateChunk() {
DCHECK(first_free_ == nullptr);
- // Do we need to resize?
- if (num_chunks_ == capacity_) {
- if (capacity_ == 0U) {
- // Initialization.
- capacity_ = kInitialChunkStorage;
- uintptr_t* new_backing = new uintptr_t[capacity_]();
- DCHECK(monitor_chunks_.LoadRelaxed() == nullptr);
- monitor_chunks_.StoreRelaxed(new_backing);
- } else {
- size_t new_capacity = 2 * capacity_;
- uintptr_t* new_backing = new uintptr_t[new_capacity]();
- uintptr_t* old_backing = monitor_chunks_.LoadRelaxed();
- memcpy(new_backing, old_backing, sizeof(uintptr_t) * capacity_);
- monitor_chunks_.StoreRelaxed(new_backing);
- capacity_ = new_capacity;
- old_chunk_arrays_.push_back(std::unique_ptr<uintptr_t[]>(old_backing));
- VLOG(monitor) << "Resizing to capacity " << capacity_;
- }
+ // Do we need to allocate another chunk list?
+ if (num_chunks_ == current_chunk_list_capacity_) {
+ if (current_chunk_list_capacity_ != 0U) {
+ ++current_chunk_list_index_;
+ CHECK_LT(current_chunk_list_index_, kMaxChunkLists) << "Out of space for inflated monitors";
+ VLOG(monitor) << "Expanding to capacity "
+ << 2 * ChunkListCapacity(current_chunk_list_index_) - kInitialChunkStorage;
+ } // else we're initializing
+ current_chunk_list_capacity_ = ChunkListCapacity(current_chunk_list_index_);
+ uintptr_t* new_list = new uintptr_t[current_chunk_list_capacity_]();
+ DCHECK(monitor_chunks_[current_chunk_list_index_] == nullptr);
+ monitor_chunks_[current_chunk_list_index_] = new_list;
+ num_chunks_ = 0;
}
// Allocate the chunk.
@@ -65,7 +64,7 @@
CHECK_EQ(0U, reinterpret_cast<uintptr_t>(chunk) % kMonitorAlignment);
// Add the chunk.
- *(monitor_chunks_.LoadRelaxed() + num_chunks_) = reinterpret_cast<uintptr_t>(chunk);
+ monitor_chunks_[current_chunk_list_index_][num_chunks_] = reinterpret_cast<uintptr_t>(chunk);
num_chunks_++;
// Set up the free list
@@ -73,8 +72,8 @@
(kChunkCapacity - 1) * kAlignedMonitorSize);
last->next_free_ = nullptr;
// Eagerly compute id.
- last->monitor_id_ = OffsetToMonitorId((num_chunks_ - 1) * kChunkSize +
- (kChunkCapacity - 1) * kAlignedMonitorSize);
+ last->monitor_id_ = OffsetToMonitorId(current_chunk_list_index_* (kMaxListSize * kChunkSize)
+ + (num_chunks_ - 1) * kChunkSize + (kChunkCapacity - 1) * kAlignedMonitorSize);
for (size_t i = 0; i < kChunkCapacity - 1; ++i) {
Monitor* before = reinterpret_cast<Monitor*>(reinterpret_cast<uintptr_t>(last) -
kAlignedMonitorSize);
@@ -91,21 +90,19 @@
void MonitorPool::FreeInternal() {
// This is on shutdown with NO_THREAD_SAFETY_ANALYSIS, can't/don't need to lock.
- uintptr_t* backing = monitor_chunks_.LoadRelaxed();
- DCHECK(backing != nullptr);
- DCHECK_GT(capacity_, 0U);
- DCHECK_GT(num_chunks_, 0U);
-
- for (size_t i = 0; i < capacity_; ++i) {
- if (i < num_chunks_) {
- DCHECK_NE(backing[i], 0U);
- allocator_.deallocate(reinterpret_cast<uint8_t*>(backing[i]), kChunkSize);
- } else {
- DCHECK_EQ(backing[i], 0U);
+ DCHECK_NE(current_chunk_list_capacity_, 0UL);
+ for (size_t i = 0; i <= current_chunk_list_index_; ++i) {
+ DCHECK_NE(monitor_chunks_[i], static_cast<uintptr_t*>(nullptr));
+ for (size_t j = 0; j < ChunkListCapacity(i); ++j) {
+ if (i < current_chunk_list_index_ || j < num_chunks_) {
+ DCHECK_NE(monitor_chunks_[i][j], 0U);
+ allocator_.deallocate(reinterpret_cast<uint8_t*>(monitor_chunks_[i][j]), kChunkSize);
+ } else {
+ DCHECK_EQ(monitor_chunks_[i][j], 0U);
+ }
}
+ delete[] monitor_chunks_[i];
}
-
- delete[] backing;
}
Monitor* MonitorPool::CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj,
diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h
index 875b3fe..99810e0 100644
--- a/runtime/monitor_pool.h
+++ b/runtime/monitor_pool.h
@@ -128,12 +128,17 @@
void ReleaseMonitorToPool(Thread* self, Monitor* monitor);
void ReleaseMonitorsToPool(Thread* self, MonitorList::Monitors* monitors);
- // Note: This is safe as we do not ever move chunks.
+ // Note: This is safe as we do not ever move chunks. All needed entries in the monitor_chunks_
+ // data structure are read-only once we get here. Updates happen-before this call because
+ // the lock word was stored with release semantics and we read it with acquire semantics to
+ // retrieve the id.
Monitor* LookupMonitor(MonitorId mon_id) {
size_t offset = MonitorIdToOffset(mon_id);
size_t index = offset / kChunkSize;
+ size_t top_index = index / kMaxListSize;
+ size_t list_index = index % kMaxListSize;
size_t offset_in_chunk = offset % kChunkSize;
- uintptr_t base = *(monitor_chunks_.LoadRelaxed()+index);
+ uintptr_t base = monitor_chunks_[top_index][list_index];
return reinterpret_cast<Monitor*>(base + offset_in_chunk);
}
@@ -142,28 +147,37 @@
return base_addr <= mon_ptr && (mon_ptr - base_addr < kChunkSize);
}
- // Note: This is safe as we do not ever move chunks.
MonitorId ComputeMonitorIdInPool(Monitor* mon, Thread* self) {
MutexLock mu(self, *Locks::allocated_monitor_ids_lock_);
- for (size_t index = 0; index < num_chunks_; ++index) {
- uintptr_t chunk_addr = *(monitor_chunks_.LoadRelaxed() + index);
- if (IsInChunk(chunk_addr, mon)) {
- return OffsetToMonitorId(
- reinterpret_cast<uintptr_t>(mon) - chunk_addr + index * kChunkSize);
+ for (size_t i = 0; i <= current_chunk_list_index_; ++i) {
+ for (size_t j = 0; j < ChunkListCapacity(i); ++j) {
+ if (j >= num_chunks_ && i == current_chunk_list_index_) {
+ break;
+ }
+ uintptr_t chunk_addr = monitor_chunks_[i][j];
+ if (IsInChunk(chunk_addr, mon)) {
+ return OffsetToMonitorId(
+ reinterpret_cast<uintptr_t>(mon) - chunk_addr
+ + i * (kMaxListSize * kChunkSize) + j * kChunkSize);
+ }
}
}
LOG(FATAL) << "Did not find chunk that contains monitor.";
return 0;
}
- static size_t MonitorIdToOffset(MonitorId id) {
+ static constexpr size_t MonitorIdToOffset(MonitorId id) {
return id << 3;
}
- static MonitorId OffsetToMonitorId(size_t offset) {
+ static constexpr MonitorId OffsetToMonitorId(size_t offset) {
return static_cast<MonitorId>(offset >> 3);
}
+ static constexpr size_t ChunkListCapacity(size_t index) {
+ return kInitialChunkStorage << index;
+ }
+
// TODO: There are assumptions in the code that monitor addresses are 8B aligned (>>3).
static constexpr size_t kMonitorAlignment = 8;
// Size of a monitor, rounded up to a multiple of alignment.
@@ -174,20 +188,47 @@
// Chunk size that is referenced in the id. We can collapse this to the actually used storage
// in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this will mean proper divisions.
static constexpr size_t kChunkSize = kPageSize;
- // The number of initial chunks storable in monitor_chunks_. The number is large enough to make
- // resizing unlikely, but small enough to not waste too much memory.
- static constexpr size_t kInitialChunkStorage = 8U;
+ static_assert(IsPowerOfTwo(kChunkSize), "kChunkSize must be power of 2");
+ // The number of chunks of storage that can be referenced by the initial chunk list.
+ // The total number of usable monitor chunks is typically 255 times this number, so it
+ // should be large enough that we don't run out. We run out of address bits if it's > 512.
+ // Currently we set it a bit smaller, to save half a page per process. We make it tiny in
+ // debug builds to catch growth errors. The only value we really expect to tune.
+ static constexpr size_t kInitialChunkStorage = kIsDebugBuild ? 1U : 256U;
+ static_assert(IsPowerOfTwo(kInitialChunkStorage), "kInitialChunkStorage must be power of 2");
+ // The number of lists, each containing pointers to storage chunks.
+ static constexpr size_t kMaxChunkLists = 8; // Dictated by 3 bit index. Don't increase above 8.
+ static_assert(IsPowerOfTwo(kMaxChunkLists), "kMaxChunkLists must be power of 2");
+ static constexpr size_t kMaxListSize = kInitialChunkStorage << (kMaxChunkLists - 1);
+ // We lose 3 bits in monitor id due to 3 bit monitor_chunks_ index, and gain it back from
+ // the 3 bit alignment constraint on monitors:
+ static_assert(kMaxListSize * kChunkSize < (1 << LockWord::kMonitorIdSize),
+ "Monitor id bits don't fit");
+ static_assert(IsPowerOfTwo(kMaxListSize), "kMaxListSize must be power of 2");
- // List of memory chunks. Each chunk is kChunkSize.
- Atomic<uintptr_t*> monitor_chunks_;
- // Number of chunks stored.
+ // Array of pointers to lists (again arrays) of pointers to chunks containing monitors.
+ // Zeroth entry points to a list (array) of kInitialChunkStorage pointers to chunks.
+ // Each subsequent list as twice as large as the preceding one.
+ // Monitor Ids are interpreted as follows:
+ // Top 3 bits (of 28): index into monitor_chunks_.
+ // Next 16 bits: index into the chunk list, i.e. monitor_chunks_[i].
+ // Last 9 bits: offset within chunk, expressed as multiple of kMonitorAlignment.
+ // If we set kInitialChunkStorage to 512, this would allow us to use roughly 128K chunks of
+ // monitors, which is 0.5GB of monitors. With this maximum setting, the largest chunk list
+ // contains 64K entries, and we make full use of the available index space. With a
+ // kInitialChunkStorage value of 256, this is proportionately reduced to 0.25GB of monitors.
+ // Updates to monitor_chunks_ are guarded by allocated_monitor_ids_lock_ .
+ // No field in this entire data structure is ever updated once a monitor id whose lookup
+ // requires it has been made visible to another thread. Thus readers never race with
+ // updates, in spite of the fact that they acquire no locks.
+ uintptr_t* monitor_chunks_[kMaxChunkLists]; // uintptr_t is really a Monitor* .
+ // Highest currently used index in monitor_chunks_ . Used for newly allocated chunks.
+ size_t current_chunk_list_index_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
+ // Number of chunk pointers stored in monitor_chunks_[current_chunk_list_index_] so far.
size_t num_chunks_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
- // Number of chunks storable.
- size_t capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
-
- // To avoid race issues when resizing, we keep all the previous arrays.
- std::vector<std::unique_ptr<uintptr_t[]>> old_chunk_arrays_
- GUARDED_BY(Locks::allocated_monitor_ids_lock_);
+ // After the initial allocation, this is always equal to
+ // ChunkListCapacity(current_chunk_list_index_).
+ size_t current_chunk_list_capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_);
typedef TrackingAllocator<uint8_t, kAllocatorTagMonitorPool> Allocator;
Allocator allocator_;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index f355c2a..5ba8df7 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -212,6 +212,10 @@
Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, static_cast<size_t>(bytes));
}
+static void VMRuntime_registerSensitiveThread(JNIEnv*, jobject) {
+ Runtime::Current()->RegisterSensitiveThread();
+}
+
static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
if (UNLIKELY(bytes < 0)) {
ScopedObjectAccess soa(env);
@@ -643,6 +647,7 @@
NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
+ NATIVE_METHOD(VMRuntime, registerSensitiveThread, "()V"),
NATIVE_METHOD(VMRuntime, registerNativeFree, "(I)V"),
NATIVE_METHOD(VMRuntime, requestConcurrentGC, "()V"),
NATIVE_METHOD(VMRuntime, requestHeapTrim, "()V"),
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 6b7ca40..0624da3 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -238,12 +238,13 @@
DCHECK(name != nullptr);
DCHECK(self != nullptr);
- StackHandleScope<1> hs(self);
+ StackHandleScope<2> hs(self);
MutableHandle<mirror::Class> h_clazz(hs.NewHandle(clazz));
+ Handle<mirror::String> h_name(hs.NewHandle(name));
// We search the current class, its direct interfaces then its superclass.
while (h_clazz.Get() != nullptr) {
- mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), name);
+ mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), h_name.Get());
if ((result != nullptr) && (result->GetAccessFlags() & kAccPublic)) {
return result;
} else if (UNLIKELY(self->IsExceptionPending())) {
@@ -258,7 +259,7 @@
self->AssertPendingException();
return nullptr;
}
- result = GetPublicFieldRecursive(self, iface, name);
+ result = GetPublicFieldRecursive(self, iface, h_name.Get());
if (result != nullptr) {
DCHECK(result->GetAccessFlags() & kAccPublic);
return result;
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 11a9d76..9470624 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -370,6 +370,10 @@
return lookup_table_data_;
}
+ const uint8_t* GetDexFilePointer() const {
+ return dex_file_pointer_;
+ }
+
~OatDexFile();
private:
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9894353..9ab0072 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -74,6 +74,20 @@
compare.release();
}
+const OatFile* OatFileManager::FindOpenedOatFileFromDexLocation(
+ const std::string& dex_base_location) const {
+ ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
+ const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
+ for (const OatDexFile* oat_dex_file : oat_dex_files) {
+ if (DexFile::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) {
+ return oat_file.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location)
const {
ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 574d0e2..f98102e 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -60,6 +60,11 @@
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) const
REQUIRES(!Locks::oat_file_manager_lock_);
+ // Find the oat file which contains a dex files with the given dex base location,
+ // returns null if there are none.
+ const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_base_location) const
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
// Attempt to reserve a location, returns false if it is already reserved or already in used by
// an oat file.
bool RegisterOatFileLocation(const std::string& oat_location)
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index c8d4291..b25a1bb 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -153,7 +153,7 @@
.Define("-Xusejit:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
- .IntoKey(M::UseJIT)
+ .IntoKey(M::UseJitCompilation)
.Define("-Xjitinitialsize:_")
.WithType<MemoryKiB>()
.IntoKey(M::JITCodeCacheInitialCapacity)
@@ -172,6 +172,9 @@
.Define("-Xjitprithreadweight:_")
.WithType<unsigned int>()
.IntoKey(M::JITPriorityThreadWeight)
+ .Define("-Xjittransitionweight:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::JITInvokeTransitionWeight)
.Define("-Xjitsaveprofilinginfo")
.WithValue(true)
.IntoKey(M::JITSaveProfilingInfo)
@@ -471,6 +474,11 @@
LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
}
+ if (args.GetOrDefault(M::UseJitCompilation) && args.GetOrDefault(M::Interpret)) {
+ Usage("-Xusejit:true and -Xint cannot be specified together");
+ Exit(0);
+ }
+
// Set a default boot class path if we didn't get an explicit one via command line.
if (getenv("BOOTCLASSPATH") != nullptr) {
args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index c7ccee2..1dea562 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -434,7 +434,7 @@
bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
InlineMethod* result) {
DCHECK(verifier != nullptr);
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
}
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index a785ecb..a3e1f00 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -509,7 +509,7 @@
// Compiled code made an explicit deoptimization.
ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod();
DCHECK(deopt_method != nullptr);
- if (Runtime::Current()->UseJit()) {
+ if (Runtime::Current()->UseJitCompilation()) {
Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor(
deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader());
} else {
@@ -611,7 +611,7 @@
// Prints out methods with their type of frame.
class DumpFramesWithTypeStackVisitor FINAL : public StackVisitor {
public:
- DumpFramesWithTypeStackVisitor(Thread* self, bool show_details = false)
+ explicit DumpFramesWithTypeStackVisitor(Thread* self, bool show_details = false)
SHARED_REQUIRES(Locks::mutator_lock_)
: StackVisitor(self, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
show_details_(show_details) {}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a4d31ef..bb19cbd 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -558,15 +558,21 @@
started_ = true;
- if (jit_options_->UseJIT()) {
+ // Create the JIT either if we have to use JIT compilation or save profiling info.
+ // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
+ // recoding profiles. Maybe we should consider changing the name to be more clear it's
+ // not only about compiling. b/28295073.
+ if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
std::string error_msg;
if (!IsZygote()) {
// If we are the zygote then we need to wait until after forking to create the code cache
// due to SELinux restrictions on r/w/x memory regions.
CreateJit();
- } else if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
- // Try to load compiler pre zygote to reduce PSS. b/27744947
- LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+ } else if (jit_options_->UseJitCompilation()) {
+ if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
+ // Try to load compiler pre zygote to reduce PSS. b/27744947
+ LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+ }
}
}
@@ -713,7 +719,11 @@
// before fork aren't attributed to an app.
heap_->ResetGcPerformanceInfo();
- if (!is_system_server && !safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) {
+
+ if (!is_system_server &&
+ !safe_mode_ &&
+ (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
+ jit_.get() == nullptr) {
// Note that when running ART standalone (not zygote, nor zygote fork),
// the jit may have already been created.
CreateJit();
@@ -1016,7 +1026,8 @@
// this case.
// If runtime_options doesn't have UseJIT set to true then CreateFromRuntimeArguments returns
// null and we don't create the jit.
- jit_options_->SetUseJIT(false);
+ jit_options_->SetUseJitCompilation(false);
+ jit_options_->SetSaveProfilingInfo(false);
}
// Allocate a global table of boxed lambda objects <-> closures.
@@ -1613,18 +1624,19 @@
}
}
-static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
-
ArtMethod* Runtime::CreateImtConflictMethod(LinearAlloc* linear_alloc) {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod(linear_alloc);
+ ClassLinker* const class_linker = GetClassLinker();
+ ArtMethod* method = class_linker->CreateRuntimeMethod(linear_alloc);
// When compiling, the code pointer will get set later when the image is loaded.
+ const size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
if (IsAotCompiler()) {
- size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
- method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
}
+ // Create empty conflict table.
+ method->SetImtConflictTable(class_linker->CreateImtConflictTable(/*count*/0u, linear_alloc),
+ pointer_size);
return method;
}
@@ -1632,9 +1644,6 @@
CHECK(method != nullptr);
CHECK(method->IsRuntimeMethod());
imt_conflict_method_ = method;
- if (!IsAotCompiler()) {
- method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
- }
}
ArtMethod* Runtime::CreateResolutionMethod() {
@@ -1915,9 +1924,8 @@
void Runtime::CreateJit() {
CHECK(!IsAotCompiler());
- if (GetInstrumentation()->IsForcedInterpretOnly()) {
- // Don't create JIT if forced interpret only.
- return;
+ if (kIsDebugBuild && GetInstrumentation()->IsForcedInterpretOnly()) {
+ DCHECK(!jit_options_->UseJitCompilation());
}
std::string error_msg;
jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
@@ -1944,8 +1952,20 @@
CHECK(method != nullptr);
CHECK(method->IsRuntimeMethod());
imt_unimplemented_method_ = method;
- if (!IsAotCompiler()) {
- method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
+}
+
+void Runtime::FixupConflictTables() {
+ // We can only do this after the class linker is created.
+ const size_t pointer_size = GetClassLinker()->GetImagePointerSize();
+ if (imt_unimplemented_method_->GetImtConflictTable(pointer_size) == nullptr) {
+ imt_unimplemented_method_->SetImtConflictTable(
+ ClassLinker::CreateImtConflictTable(/*count*/0u, GetLinearAlloc(), pointer_size),
+ pointer_size);
+ }
+ if (imt_conflict_method_->GetImtConflictTable(pointer_size) == nullptr) {
+ imt_conflict_method_->SetImtConflictTable(
+ ClassLinker::CreateImtConflictTable(/*count*/0u, GetLinearAlloc(), pointer_size),
+ pointer_size);
}
}
@@ -1981,4 +2001,18 @@
GetHeap()->UpdateProcessState(old_process_state, process_state);
}
+void Runtime::RegisterSensitiveThread() const {
+ Thread::SetJitSensitiveThread();
+}
+
+// Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
+bool Runtime::UseJitCompilation() const {
+ return (jit_ != nullptr) && jit_->UseJitCompilation();
+}
+
+// Returns true if profile saving is enabled. GetJit() will be not null in this case.
+bool Runtime::SaveProfileInfo() const {
+ return (jit_ != nullptr) && jit_->SaveProfilingInfo();
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ae25dd1..1394462 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -127,7 +127,7 @@
// IsAotCompiler for compilers that don't have a running runtime. Only dex2oat currently.
bool IsAotCompiler() const {
- return !UseJit() && IsCompiler();
+ return !UseJitCompilation() && IsCompiler();
}
// IsCompiler is any runtime which has a running compiler, either dex2oat or JIT.
@@ -383,6 +383,7 @@
return imt_conflict_method_ != nullptr;
}
+ void FixupConflictTables();
void SetImtConflictMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
void SetImtUnimplementedMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -451,9 +452,11 @@
jit::Jit* GetJit() {
return jit_.get();
}
- bool UseJit() const {
- return jit_.get() != nullptr;
- }
+
+ // Returns true if JIT compilations are enabled. GetJit() will be not null in this case.
+ bool UseJitCompilation() const;
+ // Returns true if profile saving is enabled. GetJit() will be not null in this case.
+ bool SaveProfileInfo() const;
void PreZygoteFork();
bool InitZygote();
@@ -635,6 +638,8 @@
return process_state_ == kProcessStateJankPerceptible;
}
+ void RegisterSensitiveThread() const;
+
void SetZygoteNoThreadSection(bool val) {
zygote_no_threads_ = val;
}
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 6433c33..2a96703 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -66,12 +66,13 @@
RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier))
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
-RUNTIME_OPTIONS_KEY (bool, UseJIT, false)
+RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false)
RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold, jit::Jit::kDefaultCompileThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITPriorityThreadWeight)
+RUNTIME_OPTIONS_KEY (unsigned int, JITInvokeTransitionWeight)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheInitialCapacity, jit::JitCodeCache::kInitialCapacity)
RUNTIME_OPTIONS_KEY (MemoryKiB, JITCodeCacheMaxCapacity, jit::JitCodeCache::kMaxCapacity)
RUNTIME_OPTIONS_KEY (bool, JITSaveProfilingInfo, false)
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 56ef5aa..a5ca527 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -637,8 +637,8 @@
// If we are the JIT then we may have just compiled the method after the
// IsQuickToInterpreterBridge check.
- jit::Jit* const jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->UseJitCompilation() && runtime->GetJit()->GetCodeCache()->ContainsPc(code)) {
return;
}
@@ -678,8 +678,10 @@
if (space->IsImageSpace()) {
auto* image_space = space->AsImageSpace();
const auto& header = image_space->GetImageHeader();
- const auto* methods = &header.GetMethodsSection();
- if (methods->Contains(reinterpret_cast<const uint8_t*>(method) - image_space->Begin())) {
+ const ImageSection& methods = header.GetMethodsSection();
+ const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
+ const size_t offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin();
+ if (methods.Contains(offset) || runtime_methods.Contains(offset)) {
in_image = true;
break;
}
@@ -948,7 +950,7 @@
}
}
-void LockCountData::AddMonitorInternal(Thread* self, mirror::Object* obj) {
+void LockCountData::AddMonitor(Thread* self, mirror::Object* obj) {
if (obj == nullptr) {
return;
}
@@ -965,7 +967,7 @@
monitors_->push_back(obj);
}
-void LockCountData::RemoveMonitorInternal(Thread* self, const mirror::Object* obj) {
+void LockCountData::RemoveMonitorOrThrow(Thread* self, const mirror::Object* obj) {
if (obj == nullptr) {
return;
}
@@ -998,7 +1000,7 @@
obj->MonitorExit(self);
}
-bool LockCountData::CheckAllMonitorsReleasedInternal(Thread* self) {
+bool LockCountData::CheckAllMonitorsReleasedOrThrow(Thread* self) {
DCHECK(self != nullptr);
if (monitors_ != nullptr) {
if (!monitors_->empty()) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 7301184..e77ab46 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -80,39 +80,18 @@
public:
// Add the given object to the list of monitors, that is, objects that have been locked. This
// will not throw (but be skipped if there is an exception pending on entry).
- template <bool kLockCounting>
- void AddMonitor(Thread* self, mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(self != nullptr);
- if (!kLockCounting) {
- return;
- }
- AddMonitorInternal(self, obj);
- }
+ void AddMonitor(Thread* self, mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
// Try to remove the given object from the monitor list, indicating an unlock operation.
// This will throw an IllegalMonitorStateException (clearing any already pending exception), in
// case that there wasn't a lock recorded for the object.
- template <bool kLockCounting>
void RemoveMonitorOrThrow(Thread* self,
- const mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(self != nullptr);
- if (!kLockCounting) {
- return;
- }
- RemoveMonitorInternal(self, obj);
- }
+ const mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
// Check whether all acquired monitors have been released. This will potentially throw an
// IllegalMonitorStateException, clearing any already pending exception. Returns true if the
// check shows that everything is OK wrt/ lock counting, false otherwise.
- template <bool kLockCounting>
- bool CheckAllMonitorsReleasedOrThrow(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) {
- DCHECK(self != nullptr);
- if (!kLockCounting) {
- return true;
- }
- return CheckAllMonitorsReleasedInternal(self);
- }
+ bool CheckAllMonitorsReleasedOrThrow(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_);
template <typename T, typename... Args>
void VisitMonitors(T visitor, Args&&... args) SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -125,12 +104,6 @@
}
private:
- // Internal implementations.
- void AddMonitorInternal(Thread* self, mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
- void RemoveMonitorInternal(Thread* self, const mirror::Object* obj)
- SHARED_REQUIRES(Locks::mutator_lock_);
- bool CheckAllMonitorsReleasedInternal(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_);
-
// Stores references to the locked-on objects. As noted, this should be visited during thread
// marking.
std::unique_ptr<std::vector<mirror::Object*>> monitors_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 7922b60..fb24828 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -89,6 +89,7 @@
ConditionVariable* Thread::resume_cond_ = nullptr;
const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA);
bool (*Thread::is_sensitive_thread_hook_)() = nullptr;
+Thread* Thread::jit_sensitive_thread_ = nullptr;
static constexpr bool kVerifyImageObjectsMarked = kIsDebugBuild;
@@ -739,7 +740,7 @@
{
MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_);
if (runtime->IsShuttingDownLocked()) {
- LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name;
+ LOG(WARNING) << "Thread attaching while runtime is shutting down: " << thread_name;
return nullptr;
} else {
Runtime::Current()->StartThreadBirth();
@@ -3010,7 +3011,6 @@
return count;
}
-
void Thread::DeoptimizeWithDeoptimizationException(JValue* result) {
DCHECK_EQ(GetException(), Thread::GetDeoptimizationException());
ClearException();
@@ -3031,4 +3031,11 @@
interpreter::EnterInterpreterFromDeoptimize(this, shadow_frame, from_code, result);
}
+void Thread::SetException(mirror::Throwable* new_exception) {
+ CHECK(new_exception != nullptr);
+ // TODO: DCHECK(!IsExceptionPending());
+ tlsPtr_.exception = new_exception;
+ // LOG(ERROR) << new_exception->Dump();
+}
+
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index ed42e46..582a0cd 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -363,12 +363,7 @@
void AssertNoPendingException() const;
void AssertNoPendingExceptionForNewException(const char* msg) const;
- void SetException(mirror::Throwable* new_exception)
- SHARED_REQUIRES(Locks::mutator_lock_) {
- CHECK(new_exception != nullptr);
- // TODO: DCHECK(!IsExceptionPending());
- tlsPtr_.exception = new_exception;
- }
+ void SetException(mirror::Throwable* new_exception) SHARED_REQUIRES(Locks::mutator_lock_);
void ClearException() SHARED_REQUIRES(Locks::mutator_lock_) {
tlsPtr_.exception = nullptr;
@@ -1098,6 +1093,12 @@
return debug_disallow_read_barrier_;
}
+ // Returns true if the current thread is the jit sensitive thread.
+ bool IsJitSensitiveThread() const {
+ return this == jit_sensitive_thread_;
+ }
+
+ // Returns true if StrictMode events are traced for the current thread.
static bool IsSensitiveThread() {
if (is_sensitive_thread_hook_ != nullptr) {
return (*is_sensitive_thread_hook_)();
@@ -1180,6 +1181,16 @@
ALWAYS_INLINE void PassActiveSuspendBarriers()
REQUIRES(!Locks::thread_suspend_count_lock_, !Roles::uninterruptible_);
+ // Registers the current thread as the jit sensitive thread. Should be called just once.
+ static void SetJitSensitiveThread() {
+ if (jit_sensitive_thread_ == nullptr) {
+ jit_sensitive_thread_ = Thread::Current();
+ } else {
+ LOG(WARNING) << "Attempt to set the sensitive thread twice. Tid:"
+ << Thread::Current()->GetTid();
+ }
+ }
+
static void SetSensitiveThreadHook(bool (*is_sensitive_thread_hook)()) {
is_sensitive_thread_hook_ = is_sensitive_thread_hook;
}
@@ -1229,6 +1240,8 @@
// Hook passed by framework which returns true
// when StrictMode events are traced for the current thread.
static bool (*is_sensitive_thread_hook_)();
+ // Stores the jit sensitive thread (which for now is the UI thread).
+ static Thread* jit_sensitive_thread_;
/***********************************************************************************************/
// Thread local storage. Fields are grouped by size to enable 32 <-> 64 searching to account for
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 472a85c..6a50b8e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1459,6 +1459,14 @@
return stat(filename.c_str(), &buffer) == 0;
}
+bool FileExistsAndNotEmpty(const std::string& filename) {
+ struct stat buffer;
+ if (stat(filename.c_str(), &buffer) != 0) {
+ return false;
+ }
+ return buffer.st_size > 0;
+}
+
std::string PrettyDescriptor(Primitive::Type type) {
return PrettyDescriptor(Primitive::Descriptor(type));
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 83ac0b8..c1e88a4 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -296,6 +296,7 @@
// Returns true if the file exists.
bool FileExists(const std::string& filename);
+bool FileExistsAndNotEmpty(const std::string& filename);
class VoidFunctor {
public:
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 647578e..d05ae42 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -396,6 +396,18 @@
<< PrettyMethod(method_idx, *dex_file) << "\n");
}
result.kind = kSoftFailure;
+ if (method != nullptr &&
+ !CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) {
+ method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
+ }
+ }
+ if (method != nullptr) {
+ if (verifier.HasInstructionThatWillThrow()) {
+ method->SetAccessFlags(method->GetAccessFlags() | kAccCompileDontBother);
+ }
+ if ((verifier.encountered_failure_types_ & VerifyError::VERIFY_ERROR_LOCKING) != 0) {
+ method->SetAccessFlags(method->GetAccessFlags() | kAccMustCountLocks);
+ }
}
} else {
// Bad method data.
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index ebb0b8c..2592a21 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -142,6 +142,14 @@
kHardFailure,
};
+ static bool CanCompilerHandleVerificationFailure(uint32_t encountered_failure_types) {
+ constexpr uint32_t unresolved_mask = verifier::VerifyError::VERIFY_ERROR_NO_CLASS
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD
+ | verifier::VerifyError::VERIFY_ERROR_ACCESS_METHOD;
+ return (encountered_failure_types & (~unresolved_mask)) == 0;
+ }
+
// Verify a class. Returns "kNoFailure" on success.
static FailureKind VerifyClass(Thread* self,
mirror::Class* klass,
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 15683b0..17a6049 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -23,6 +23,7 @@
public class Main {
static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/141-class-unload-ex.jar";
+ static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
static String nativeLibraryName;
public static void main(String[] args) throws Exception {
@@ -32,7 +33,7 @@
throw new AssertionError("Couldn't find path class loader class");
}
Constructor constructor =
- pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+ pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
try {
testUnloadClass(constructor);
testUnloadLoader(constructor);
@@ -49,7 +50,7 @@
// Test that the oat files are unloaded.
testOatFilesUnloaded(getPid());
} catch (Exception e) {
- System.out.println(e);
+ e.printStackTrace();
}
}
@@ -118,7 +119,7 @@
private static void testNoUnloadInvoke(Constructor constructor) throws Exception {
WeakReference<ClassLoader> loader =
new WeakReference((ClassLoader) constructor.newInstance(
- DEX_FILE, ClassLoader.getSystemClassLoader()));
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()));
WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
intHolder.get().getDeclaredMethod("runGC").invoke(intHolder.get());
boolean isNull = loader.get() == null;
@@ -128,7 +129,7 @@
private static void testNoUnloadInstance(Constructor constructor) throws Exception {
WeakReference<ClassLoader> loader =
new WeakReference((ClassLoader) constructor.newInstance(
- DEX_FILE, ClassLoader.getSystemClassLoader()));
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()));
WeakReference<Class> intHolder = new WeakReference(loader.get().loadClass("IntHolder"));
Object o = intHolder.get().newInstance();
Runtime.getRuntime().gc();
@@ -138,7 +139,7 @@
private static WeakReference<Class> setUpUnloadClass(Constructor constructor) throws Exception {
ClassLoader loader = (ClassLoader) constructor.newInstance(
- DEX_FILE, ClassLoader.getSystemClassLoader());
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Class intHolder = loader.loadClass("IntHolder");
Method getValue = intHolder.getDeclaredMethod("getValue");
Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
@@ -155,7 +156,7 @@
boolean waitForCompilation)
throws Exception {
ClassLoader loader = (ClassLoader) constructor.newInstance(
- DEX_FILE, ClassLoader.getSystemClassLoader());
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Class intHolder = loader.loadClass("IntHolder");
Method setValue = intHolder.getDeclaredMethod("setValue", Integer.TYPE);
setValue.invoke(intHolder, 2);
@@ -177,7 +178,7 @@
private static WeakReference<ClassLoader> setUpLoadLibrary(Constructor constructor)
throws Exception {
ClassLoader loader = (ClassLoader) constructor.newInstance(
- DEX_FILE, ClassLoader.getSystemClassLoader());
+ DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Class intHolder = loader.loadClass("IntHolder");
Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
loadLibrary.invoke(intHolder, nativeLibraryName);
diff --git a/test/444-checker-nce/src/Main.java b/test/444-checker-nce/src/Main.java
index c96b18c..ddc2f77 100644
--- a/test/444-checker-nce/src/Main.java
+++ b/test/444-checker-nce/src/Main.java
@@ -28,10 +28,6 @@
}
/// CHECK-START: Main Main.thisTest() builder (after)
- /// CHECK: NullCheck
- /// CHECK: InvokeStaticOrDirect
-
- /// CHECK-START: Main Main.thisTest() instruction_simplifier (after)
/// CHECK-NOT: NullCheck
/// CHECK: InvokeStaticOrDirect
public Main thisTest() {
@@ -40,12 +36,10 @@
/// CHECK-START: Main Main.newInstanceRemoveTest() builder (after)
/// CHECK: NewInstance
- /// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
- /// CHECK: NullCheck
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: Main Main.newInstanceRemoveTest() instruction_simplifier (after)
+ /// CHECK-START: Main Main.newInstanceRemoveTest() builder (after)
/// CHECK-NOT: NullCheck
public Main newInstanceRemoveTest() {
Main m = new Main();
@@ -54,13 +48,10 @@
/// CHECK-START: Main Main.newArrayRemoveTest() builder (after)
/// CHECK: NewArray
- /// CHECK: NullCheck
/// CHECK: ArrayGet
- /// CHECK-START: Main Main.newArrayRemoveTest() instruction_simplifier (after)
- /// CHECK: NewArray
+ /// CHECK-START: Main Main.newArrayRemoveTest() builder (after)
/// CHECK-NOT: NullCheck
- /// CHECK: ArrayGet
public Main newArrayRemoveTest() {
Main[] ms = new Main[1];
return ms[0];
@@ -179,9 +170,6 @@
}
/// CHECK-START: Main Main.scopeRemoveTest(int, Main) builder (after)
- /// CHECK: NullCheck
-
- /// CHECK-START: Main Main.scopeRemoveTest(int, Main) instruction_simplifier (after)
/// CHECK-NOT: NullCheck
public Main scopeRemoveTest(int count, Main a) {
Main m = null;
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 66e1d92..41771b5 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -927,6 +927,32 @@
}
}
+ /// CHECK-START: void Main.nonzeroLength(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.nonzeroLength(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ public static void nonzeroLength(int[] a) {
+ if (a.length != 0) {
+ a[0] = 112;
+ }
+ }
+
+ /// CHECK-START: void Main.knownLength(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.knownLength(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ public static void knownLength(int[] a) {
+ if (a.length == 2) {
+ a[0] = -1;
+ a[1] = -2;
+ }
+ }
+
static int[][] mA;
/// CHECK-START: void Main.dynamicBCEAndIntrinsic(int) BCE (before)
@@ -1586,6 +1612,26 @@
}
}
+ nonzeroLength(array);
+ if (array[0] != 112) {
+ System.out.println("nonzero length failed!");
+ }
+
+ knownLength(array);
+ if (array[0] != 112 || array[1] != 1) {
+ System.out.println("nonzero length failed!");
+ }
+ array = new int[2];
+ knownLength(array);
+ if (array[0] != -1 || array[1] != -2) {
+ System.out.println("nonzero length failed!");
+ }
+
+ // Zero length array does not break.
+ array = new int[0];
+ nonzeroLength(array);
+ knownLength(array);
+
mA = new int[4][4];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index c644692..b12fbd6 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -710,8 +710,8 @@
// making them a candidate for deoptimization based on constant indices.
// Compiler should ensure the array loads are not subsequently hoisted
// "above" the deoptimization "barrier" on the bounds.
- a[0][i] = 1;
- a[1][i] = 2;
+ a[1][i] = 1;
+ a[2][i] = 2;
a[99][i] = 3;
}
}
@@ -1042,11 +1042,11 @@
a = new int[100][10];
expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
for (int i = 0; i < 10; i++) {
- expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]);
- expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
+ expectEquals((i % 10) != 0 ? 1 : 0, a[1][i]);
+ expectEquals((i % 10) != 0 ? 2 : 0, a[2][i]);
expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
}
- a = new int[2][10];
+ a = new int[3][10];
sResult = 0;
try {
expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
@@ -1054,8 +1054,8 @@
sResult = 1;
}
expectEquals(1, sResult);
- expectEquals(a[0][1], 1);
- expectEquals(a[1][1], 2);
+ expectEquals(a[1][1], 1);
+ expectEquals(a[2][1], 2);
// Dynamic BCE combined with constant indices of all types.
boolean[] x1 = { true };
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index edb8a68..a4561b8 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -500,9 +500,9 @@
assertIntEquals(a + $noinline$IntShl(b, 16), a + (b << 16));
assertIntEquals(a + $noinline$IntShl(b, 30), a + (b << 30));
assertIntEquals(a + $noinline$IntShl(b, 31), a + (b << 31));
- assertIntEquals(a + $noinline$IntShl(b, 32), a + (b << 32));
- assertIntEquals(a + $noinline$IntShl(b, 62), a + (b << 62));
- assertIntEquals(a + $noinline$IntShl(b, 63), a + (b << 63));
+ assertIntEquals(a + $noinline$IntShl(b, 32), a + (b << $opt$inline$IntConstant32()));
+ assertIntEquals(a + $noinline$IntShl(b, 62), a + (b << $opt$inline$IntConstant62()));
+ assertIntEquals(a + $noinline$IntShl(b, 63), a + (b << $opt$inline$IntConstant63()));
assertIntEquals(a - $noinline$IntShr(b, 1), a - (b >> 1));
assertIntEquals(a - $noinline$IntShr(b, 6), a - (b >> 6));
@@ -513,9 +513,9 @@
assertIntEquals(a - $noinline$IntShr(b, 16), a - (b >> 16));
assertIntEquals(a - $noinline$IntShr(b, 30), a - (b >> 30));
assertIntEquals(a - $noinline$IntShr(b, 31), a - (b >> 31));
- assertIntEquals(a - $noinline$IntShr(b, 32), a - (b >> 32));
- assertIntEquals(a - $noinline$IntShr(b, 62), a - (b >> 62));
- assertIntEquals(a - $noinline$IntShr(b, 63), a - (b >> 63));
+ assertIntEquals(a - $noinline$IntShr(b, 32), a - (b >> $opt$inline$IntConstant32()));
+ assertIntEquals(a - $noinline$IntShr(b, 62), a - (b >> $opt$inline$IntConstant62()));
+ assertIntEquals(a - $noinline$IntShr(b, 63), a - (b >> $opt$inline$IntConstant63()));
assertIntEquals(a ^ $noinline$IntUshr(b, 1), a ^ (b >>> 1));
assertIntEquals(a ^ $noinline$IntUshr(b, 6), a ^ (b >>> 6));
@@ -526,11 +526,17 @@
assertIntEquals(a ^ $noinline$IntUshr(b, 16), a ^ (b >>> 16));
assertIntEquals(a ^ $noinline$IntUshr(b, 30), a ^ (b >>> 30));
assertIntEquals(a ^ $noinline$IntUshr(b, 31), a ^ (b >>> 31));
- assertIntEquals(a ^ $noinline$IntUshr(b, 32), a ^ (b >>> 32));
- assertIntEquals(a ^ $noinline$IntUshr(b, 62), a ^ (b >>> 62));
- assertIntEquals(a ^ $noinline$IntUshr(b, 63), a ^ (b >>> 63));
+ assertIntEquals(a ^ $noinline$IntUshr(b, 32), a ^ (b >>> $opt$inline$IntConstant32()));
+ assertIntEquals(a ^ $noinline$IntUshr(b, 62), a ^ (b >>> $opt$inline$IntConstant62()));
+ assertIntEquals(a ^ $noinline$IntUshr(b, 63), a ^ (b >>> $opt$inline$IntConstant63()));
}
+ // Hiding constants outside the range [0, 32) used for int shifts from Jack.
+ // (Jack extracts only the low 5 bits.)
+ public static int $opt$inline$IntConstant32() { return 32; }
+ public static int $opt$inline$IntConstant62() { return 62; }
+ public static int $opt$inline$IntConstant63() { return 63; }
+
static long $noinline$LongShl(long b, long c) {
if (doThrow) throw new Error();
diff --git a/test/557-checker-instruction-simplifier-ror/src/Main.java b/test/557-checker-instruction-simplifier-ror/src/Main.java
index 027f262..6d8b74d 100644
--- a/test/557-checker-instruction-simplifier-ror/src/Main.java
+++ b/test/557-checker-instruction-simplifier-ror/src/Main.java
@@ -175,7 +175,7 @@
// (i >>> #distance) | (i << #-distance)
- /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier (before)
+ /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier_after_bce (before)
/// CHECK: <<ArgValue:i\d+>> ParameterValue
/// CHECK: <<Const2:i\d+>> IntConstant 2
/// CHECK: <<ConstNeg2:i\d+>> IntConstant -2
@@ -184,19 +184,23 @@
/// CHECK: <<Or:i\d+>> Or [<<UShr>>,<<Shl>>]
/// CHECK: Return [<<Or>>]
- /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier_after_bce (after)
/// CHECK: <<ArgValue:i\d+>> ParameterValue
/// CHECK: <<Const2:i\d+>> IntConstant 2
/// CHECK: <<Ror:i\d+>> Ror [<<ArgValue>>,<<Const2>>]
/// CHECK: Return [<<Ror>>]
- /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier (after)
+ /// CHECK-START: int Main.ror_int_constant_c_negc(int) instruction_simplifier_after_bce (after)
/// CHECK-NOT: UShr
/// CHECK-NOT: Shl
public static int ror_int_constant_c_negc(int value) {
- return (value >>> 2) | (value << -2);
+ return (value >>> 2) | (value << $opt$inline$IntConstantM2());
}
+ // Hiding constants outside the range [0, 32) used for int shifts from Jack.
+ // (Jack extracts only the low 5 bits.)
+ public static int $opt$inline$IntConstantM2() { return -2; }
+
// (j >>> #distance) | (j << #-distance)
/// CHECK-START: long Main.ror_long_constant_c_negc(long) instruction_simplifier (before)
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
index 167a575..7b2c6cb 100644
--- a/test/566-polymorphic-inlining/polymorphic_inline.cc
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -60,6 +60,7 @@
do_checks(cls, "testInvokeVirtual");
do_checks(cls, "testInvokeInterface");
+ do_checks(cls, "$noinline$testInlineToSameTarget");
}
} // namespace art
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index 7283e86..286f0d9 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -25,6 +25,12 @@
}
}
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
public static void main(String[] args) throws Exception {
System.loadLibrary(args[0]);
Main[] mains = new Main[3];
@@ -41,6 +47,8 @@
testInvokeVirtual(mains[1]);
testInvokeInterface(itfs[0]);
testInvokeInterface(itfs[1]);
+ $noinline$testInlineToSameTarget(mains[0]);
+ $noinline$testInlineToSameTarget(mains[1]);
}
ensureJittedAndPolymorphicInline();
@@ -56,6 +64,10 @@
// This will trigger a deoptimization of the compiled code.
assertEquals(OtherSubclass.class, testInvokeVirtual(mains[2]));
assertEquals(OtherSubclass.class, testInvokeInterface(itfs[2]));
+
+ // Run this once to make sure we execute the JITted code.
+ $noinline$testInlineToSameTarget(mains[0]);
+ assertEquals(20001, counter);
}
public Class sameInvokeVirtual() {
@@ -76,9 +88,20 @@
return m.sameInvokeVirtual();
}
+ public static void $noinline$testInlineToSameTarget(Main m) {
+ if (doThrow) throw new Error("");
+ m.increment();
+ }
+
public Object field = new Object();
public static native void ensureJittedAndPolymorphicInline();
+
+ public void increment() {
+ counter++;
+ }
+ public static int counter = 0;
+ public static boolean doThrow = false;
}
class Subclass extends Main {
diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc
index 2a5b2c9..bd8f0a9 100644
--- a/test/570-checker-osr/osr.cc
+++ b/test/570-checker-osr/osr.cc
@@ -75,7 +75,7 @@
extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInInterpreter(JNIEnv* env,
jclass,
jstring method_name) {
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
// The return value is irrelevant if we're not using JIT.
return false;
}
@@ -111,7 +111,7 @@
extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasProfilingInfo(JNIEnv* env,
jclass,
jstring method_name) {
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
return;
}
ScopedUtfChars chars(env, method_name);
@@ -151,7 +151,7 @@
extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv* env,
jclass,
jstring method_name) {
- if (!Runtime::Current()->UseJit()) {
+ if (!Runtime::Current()->UseJitCompilation()) {
return;
}
ScopedUtfChars chars(env, method_name);
diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java
index 200b54a..15c232d 100644
--- a/test/570-checker-osr/src/Main.java
+++ b/test/570-checker-osr/src/Main.java
@@ -61,6 +61,18 @@
throw new Error("Unexpected return value");
}
+ $noinline$inlineCache2(new Main(), /* isSecondInvocation */ false);
+ if ($noinline$inlineCache2(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) {
+ throw new Error("Unexpected return value");
+ }
+
+ // Test polymorphic inline cache to the same target (inlineCache3).
+ $noinline$inlineCache3(new Main(), /* isSecondInvocation */ false);
+ $noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ false);
+ if ($noinline$inlineCache3(new SubMain(), /* isSecondInvocation */ true) != null) {
+ throw new Error("Unexpected return value");
+ }
+
$noinline$stackOverflow(new Main(), /* isSecondInvocation */ false);
$noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true);
@@ -147,10 +159,76 @@
return other.returnClass();
}
+ public static Class $noinline$inlineCache2(Main m, boolean isSecondInvocation) {
+ // If we are running in non-JIT mode, or were unlucky enough to get this method
+ // already JITted, just return the expected value.
+ if (!isInInterpreter("$noinline$inlineCache2")) {
+ return SubMain.class;
+ }
+
+ ensureHasProfilingInfo("$noinline$inlineCache2");
+
+ // Ensure that we have OSR code to jump to.
+ if (isSecondInvocation) {
+ ensureHasOsrCode("$noinline$inlineCache2");
+ }
+
+ // This call will be optimized in the OSR compiled code
+ // to check and deoptimize if m is not of type 'Main'.
+ Main other = m.inlineCache2();
+
+ // Jump to OSR compiled code. The second run
+ // of this method will have 'm' as a SubMain, and the compiled
+ // code we are jumping to will have wrongly optimize other as being null.
+ if (isSecondInvocation) {
+ while (!isInOsrCode("$noinline$inlineCache2")) {}
+ }
+
+ // We used to wrongly optimize this code and assume 'other' was always null.
+ return (other == null) ? null : other.returnClass();
+ }
+
+ public static Class $noinline$inlineCache3(Main m, boolean isSecondInvocation) {
+ // If we are running in non-JIT mode, or were unlucky enough to get this method
+ // already JITted, just return the expected value.
+ if (!isInInterpreter("$noinline$inlineCache3")) {
+ return null;
+ }
+
+ ensureHasProfilingInfo("$noinline$inlineCache3");
+
+ // Ensure that we have OSR code to jump to.
+ if (isSecondInvocation) {
+ ensureHasOsrCode("$noinline$inlineCache3");
+ }
+
+ // This call will be optimized in the OSR compiled code
+ // to check and deoptimize if m is not of type 'Main'.
+ Main other = m.inlineCache3();
+
+ // Jump to OSR compiled code. The second run
+ // of this method will have 'm' as a SubMain, and the compiled
+ // code we are jumping to will have wrongly optimize other as being null.
+ if (isSecondInvocation) {
+ while (!isInOsrCode("$noinline$inlineCache3")) {}
+ }
+
+ // We used to wrongly optimize this code and assume 'other' was always null.
+ return (other == null) ? null : other.returnClass();
+ }
+
public Main inlineCache() {
return new Main();
}
+ public Main inlineCache2() {
+ return null;
+ }
+
+ public Main inlineCache3() {
+ return null;
+ }
+
public Class returnClass() {
return Main.class;
}
@@ -235,6 +313,10 @@
return new SubMain();
}
+ public Main inlineCache2() {
+ return new SubMain();
+ }
+
public void otherInlineCache() {
return;
}
diff --git a/test/572-checker-array-get-regression/src/Main.java b/test/572-checker-array-get-regression/src/Main.java
index b55be70..89b97ed 100644
--- a/test/572-checker-array-get-regression/src/Main.java
+++ b/test/572-checker-array-get-regression/src/Main.java
@@ -25,13 +25,11 @@
/// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288
/// CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1
/// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>]
- /// CHECK-DAG: <<NullCheck1:l\d+>> NullCheck [<<Array>>]
- /// CHECK-DAG: <<Length1:i\d+>> ArrayLength [<<NullCheck1>>]
+ /// CHECK-DAG: <<Length1:i\d+>> ArrayLength [<<Array>>]
/// CHECK-DAG: <<Index:i\d+>> Add [<<Length1>>,<<ConstM1>>]
- /// CHECK-DAG: <<NullCheck2:l\d+>> NullCheck [<<Array>>]
- /// CHECK-DAG: <<Length2:i\d+>> ArrayLength [<<NullCheck2>>]
+ /// CHECK-DAG: <<Length2:i\d+>> ArrayLength [<<Array>>]
/// CHECK-DAG: <<BoundsCheck:i\d+>> BoundsCheck [<<Index>>,<<Length2>>]
- /// CHECK-DAG: <<LastElement:l\d+>> ArrayGet [<<NullCheck2>>,<<BoundsCheck>>]
+ /// CHECK-DAG: <<LastElement:l\d+>> ArrayGet [<<Array>>,<<BoundsCheck>>]
/// CHECK-DAG: Return [<<LastElement>>]
diff --git a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
index 366c7b9..ef53ee8 100644
--- a/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
+++ b/test/594-checker-irreducible-linorder/smali/IrreducibleLoop.smali
@@ -67,3 +67,57 @@
return p3
.end method
+
+## CHECK-START: int IrreducibleLoop.liveness2(boolean, boolean, boolean, int) builder (after)
+## CHECK-DAG: Mul loop:<<Loop:B\d+>>
+## CHECK-DAG: Not loop:<<Loop>>
+
+## CHECK-START: int IrreducibleLoop.liveness2(boolean, boolean, boolean, int) liveness (after)
+## CHECK-DAG: Mul liveness:<<LPreEntry2:\d+>>
+## CHECK-DAG: Not liveness:<<LBackEdge1:\d+>>
+## CHECK-EVAL: <<LBackEdge1>> < <<LPreEntry2>>
+
+.method public liveness2(ZZZI)I
+ .registers 10
+
+ const v1, 1
+
+ :header1
+ if-eqz p0, :body1
+
+ :exit
+ return p3
+
+ :body1
+ # The test will generate an incorrect linear order when the following IF swaps
+ # its successors. To do that, load a boolean value and compare NotEqual to 1.
+ sget-boolean v2, LIrreducibleLoop;->f:Z
+ const v3, 1
+ if-ne v2, v3, :pre_header2
+
+ :pre_entry2
+ # This constant has a use in a phi in :back_edge2 and a back edge use in
+ # :back_edge1. Because the linear order is wrong, the back edge use has
+ # a lower liveness than the phi use.
+ const v0, 42
+ mul-int/2addr p3, p3
+ goto :back_edge2
+
+ :back_edge2
+ add-int/2addr p3, v0
+ add-int/2addr v0, v1
+ goto :header2
+
+ :header2
+ if-eqz p2, :back_edge2
+
+ :back_edge1
+ not-int p3, p3
+ goto :header1
+
+ :pre_header2
+ const v0, 42
+ goto :header2
+.end method
+
+.field public static f:Z
diff --git a/test/595-error-class/expected.txt b/test/595-error-class/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/595-error-class/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/595-error-class/info.txt b/test/595-error-class/info.txt
new file mode 100644
index 0000000..a58b8b3
--- /dev/null
+++ b/test/595-error-class/info.txt
@@ -0,0 +1 @@
+Regression test on merging array type with error component type.
diff --git a/test/595-error-class/smali/error.smali b/test/595-error-class/smali/error.smali
new file mode 100644
index 0000000..925c34b
--- /dev/null
+++ b/test/595-error-class/smali/error.smali
@@ -0,0 +1,23 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public final LAnError;
+
+.super LSuperOfAnError;
+
+# Override a final method to put this class in the error state.
+.method public foo()V
+ .registers 1
+ return-void
+.end method
diff --git a/test/595-error-class/smali/merge.smali b/test/595-error-class/smali/merge.smali
new file mode 100644
index 0000000..2f8b415
--- /dev/null
+++ b/test/595-error-class/smali/merge.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LMerge;
+.super Ljava/lang/Object;
+
+# Method that selects between x = new Integer[] or new AnError[],
+# Reference type propagation should correctly see error in component type.
+.method public static select(Z)Ljava/lang/Object;
+ .registers 2
+ const/16 v0, 10
+ if-eqz v1, :Skip
+ new-array v0, v0, [LAnError;
+ goto :Done
+:Skip
+ new-array v0, v0, [Ljava/lang/Integer;
+:Done
+ return-object v0
+.end method
diff --git a/test/595-error-class/smali/super.smali b/test/595-error-class/smali/super.smali
new file mode 100644
index 0000000..da7467d
--- /dev/null
+++ b/test/595-error-class/smali/super.smali
@@ -0,0 +1,22 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSuperOfAnError;
+
+.super Ljava/lang/Object;
+
+.method public final foo()V
+ .registers 1
+ return-void
+.end method
diff --git a/test/595-error-class/src/Main.java b/test/595-error-class/src/Main.java
new file mode 100644
index 0000000..655fa43
--- /dev/null
+++ b/test/595-error-class/src/Main.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+
+public class Main {
+
+ public static void main(String args[]) throws Throwable {
+ Class<?> c = Class.forName("Merge");
+ Method m = c.getMethod("select", boolean.class);
+ Object x = m.invoke(null, true);
+ if (x == null) {
+ throw new Error("Did not get array");
+ }
+ System.out.println("passed");
+ }
+}
diff --git a/test/595-profile-saving/expected.txt b/test/595-profile-saving/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/595-profile-saving/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/595-profile-saving/info.txt b/test/595-profile-saving/info.txt
new file mode 100644
index 0000000..5d318f5
--- /dev/null
+++ b/test/595-profile-saving/info.txt
@@ -0,0 +1 @@
+Check that profile recording works even when JIT compilation is not enabled.
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
new file mode 100644
index 0000000..a7e6f8b
--- /dev/null
+++ b/test/595-profile-saving/profile-saving.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex_file.h"
+
+#include "jit/offline_profiling_info.h"
+#include "jit/profile_saver.h"
+#include "jni.h"
+#include "method_reference.h"
+#include "mirror/class-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "scoped_thread_state_change.h"
+#include "ScopedUtfChars.h"
+#include "thread.h"
+
+namespace art {
+namespace {
+
+class CreateProfilingInfoVisitor : public StackVisitor {
+ public:
+ explicit CreateProfilingInfoVisitor(Thread* thread, const char* method_name)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+ method_name_(method_name) {}
+
+ bool VisitFrame() SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+ std::string m_name(m->GetName());
+
+ if (m_name.compare(method_name_) == 0) {
+ ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true);
+ method_index_ = m->GetDexMethodIndex();
+ return false;
+ }
+ return true;
+ }
+
+ int method_index_ = -1;
+ const char* const method_name_;
+};
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_ensureProfilingInfo(JNIEnv* env,
+ jclass,
+ jstring method_name) {
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ CreateProfilingInfoVisitor visitor(soa.Self(), chars.c_str());
+ visitor.WalkStack();
+ return visitor.method_index_;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfileProcessing(JNIEnv*, jclass) {
+ ProfileSaver::ForceProcessProfiles();
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(
+ JNIEnv* env, jclass cls, jstring filename, jint method_index) {
+ ScopedUtfChars filename_chars(env, filename);
+ CHECK(filename_chars.c_str() != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ const DexFile* dex_file = soa.Decode<mirror::Class*>(cls)->GetDexCache()->GetDexFile();
+ return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
+ dex_file,
+ static_cast<uint16_t>(method_index));
+}
+
+} // namespace
+} // namespace art
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
new file mode 100644
index 0000000..f12fac9
--- /dev/null
+++ b/test/595-profile-saving/run
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Use
+# --compiler-filter=interpret-only to make sure that the test is not compiled AOT
+# -XOatFileManagerCompilerFilter:interpret-only to make sure the test is not compiled
+# when loaded (by PathClassLoader)
+# -Xjitsaveprofilinginfo to enable profile saving
+# -Xusejit:false to disable jit and only test profiles.
+exec ${RUN} \
+ -Xcompiler-option --compiler-filter=interpret-only \
+ --runtime-option -XOatFileManagerCompilerFilter:interpret-only \
+ --runtime-option -Xjitsaveprofilinginfo \
+ --runtime-option -Xusejit:false \
+ "${@}"
diff --git a/test/595-profile-saving/src/Main.java b/test/595-profile-saving/src/Main.java
new file mode 100644
index 0000000..039503f
--- /dev/null
+++ b/test/595-profile-saving/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ File file = null;
+ try {
+ file = createTempFile();
+ // String codePath = getDexBaseLocation();
+ String codePath = System.getenv("DEX_LOCATION") + "/595-profile-saving.jar";
+ VMRuntime.registerAppInfo(file.getPath(),
+ System.getenv("DEX_LOCATION"),
+ new String[] {codePath},
+ /* foreignProfileDir */ null);
+
+ int methodIdx = $opt$noinline$testProfile();
+ ensureProfileProcessing();
+ if (!presentInProfile(file.getPath(), methodIdx)) {
+ throw new RuntimeException("Method with index " + methodIdx + " not in the profile");
+ }
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+ }
+
+ public static int $opt$noinline$testProfile() {
+ if (doThrow) throw new Error();
+ // Make sure we have a profile info for this method without the need to loop.
+ return ensureProfilingInfo("$opt$noinline$testProfile");
+ }
+
+ // Return the dex method index.
+ public static native int ensureProfilingInfo(String methodName);
+ // Ensures the profile saver does its usual processing.
+ public static native void ensureProfileProcessing();
+ // Checks if the profiles saver knows about the method.
+ public static native boolean presentInProfile(String profile, int methodIdx);
+
+ public static boolean doThrow = false;
+ private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+ static native String getProfileInfoDump(
+ String filename);
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+
+ private static class VMRuntime {
+ private static final Method registerAppInfoMethod;
+ static {
+ try {
+ Class<? extends Object> c = Class.forName("dalvik.system.VMRuntime");
+ registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+ String.class, String.class, String[].class, String.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String profile, String appDir,
+ String[] codePaths, String foreignDir) throws Exception {
+ registerAppInfoMethod.invoke(null, profile, appDir, codePaths, foreignDir);
+ }
+ }
+}
diff --git a/test/596-app-images/app_images.cc b/test/596-app-images/app_images.cc
new file mode 100644
index 0000000..11c0f42
--- /dev/null
+++ b/test/596-app-images/app_images.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "gc/space/space-inl.h"
+#include "image.h"
+#include "jni.h"
+#include "mirror/class.h"
+#include "runtime.h"
+#include "scoped_thread_state_change.h"
+
+namespace art {
+
+namespace {
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkAppImageLoaded(JNIEnv*, jclass) {
+ ScopedObjectAccess soa(Thread::Current());
+ for (auto* space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) {
+ if (space->IsImageSpace()) {
+ auto* image_space = space->AsImageSpace();
+ const auto& image_header = image_space->GetImageHeader();
+ if (image_header.IsAppImage()) {
+ return JNI_TRUE;
+ }
+ }
+ }
+ return JNI_FALSE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkAppImageContains(JNIEnv*, jclass, jclass c) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* klass_ptr = soa.Decode<mirror::Class*>(c);
+ for (auto* space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) {
+ if (space->IsImageSpace()) {
+ auto* image_space = space->AsImageSpace();
+ const auto& image_header = image_space->GetImageHeader();
+ if (image_header.IsAppImage()) {
+ if (image_space->HasAddress(klass_ptr)) {
+ return JNI_TRUE;
+ }
+ }
+ }
+ }
+ return JNI_FALSE;
+}
+
+} // namespace
+
+} // namespace art
diff --git a/test/596-app-images/expected.txt b/test/596-app-images/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/596-app-images/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/596-app-images/info.txt b/test/596-app-images/info.txt
new file mode 100644
index 0000000..a3d5e7e
--- /dev/null
+++ b/test/596-app-images/info.txt
@@ -0,0 +1 @@
+Tests that app-images are loaded and used.
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
new file mode 100644
index 0000000..75b31b8
--- /dev/null
+++ b/test/596-app-images/src/Main.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+ static class Inner {
+ public static int abc = 0;
+ }
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ if (!checkAppImageLoaded()) {
+ System.out.println("App image is not loaded!");
+ } else if (!checkAppImageContains(Inner.class)) {
+ System.out.println("App image does not contain Inner!");
+ }
+ }
+
+ public static native boolean checkAppImageLoaded();
+ public static native boolean checkAppImageContains(Class<?> klass);
+}
diff --git a/test/596-checker-dead-phi/expected.txt b/test/596-checker-dead-phi/expected.txt
new file mode 100644
index 0000000..d81cc07
--- /dev/null
+++ b/test/596-checker-dead-phi/expected.txt
@@ -0,0 +1 @@
+42
diff --git a/test/596-checker-dead-phi/info.txt b/test/596-checker-dead-phi/info.txt
new file mode 100644
index 0000000..7f7cf0f
--- /dev/null
+++ b/test/596-checker-dead-phi/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing where we used to replace a dead loop
+phi with its first incoming input.
diff --git a/test/596-checker-dead-phi/smali/IrreducibleLoop.smali b/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..bab2ba9
--- /dev/null
+++ b/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
@@ -0,0 +1,74 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Test case where liveness analysis produces linear order where loop blocks are
+# not adjacent. This revealed a bug in our SSA builder, where a dead loop phi would
+# be replaced by its incoming input during SsaRedundantPhiElimination.
+
+# Check that the outer loop suspend check environment only has the parameter vreg.
+## CHECK-START: int IrreducibleLoop.liveness(int) builder (after)
+## CHECK-DAG: <<Phi:i\d+>> Phi reg:4 loop:{{B\d+}} irreducible:false
+## CHECK-DAG: SuspendCheck env:[[_,_,_,_,<<Phi>>]] loop:{{B\d+}} irreducible:false
+
+# Check that the linear order has non-adjacent loop blocks.
+## CHECK-START: int IrreducibleLoop.liveness(int) liveness (after)
+## CHECK-DAG: Mul liveness:<<LPreEntry2:\d+>>
+## CHECK-DAG: Add liveness:<<LBackEdge1:\d+>>
+## CHECK-EVAL: <<LBackEdge1>> < <<LPreEntry2>>
+
+.method public static liveness(I)I
+ .registers 5
+
+ const-string v1, "MyString"
+
+ :header1
+ if-eqz p0, :body1
+
+ :exit
+ return p0
+
+ :body1
+ # The test will generate an incorrect linear order when the following IF swaps
+ # its successors. To do that, load a boolean value and compare NotEqual to 1.
+ sget-boolean v2, LIrreducibleLoop;->f:Z
+ const v3, 1
+ if-ne v2, v3, :pre_header2
+
+ :pre_entry2
+ # Add a marker on the irreducible loop entry.
+ mul-int/2addr p0, p0
+ goto :back_edge2
+
+ :back_edge2
+ goto :header2
+
+ :header2
+ if-eqz p0, :back_edge2
+
+ :back_edge1
+ # Add a marker on the outer loop back edge.
+ add-int/2addr p0, p0
+ # Set a wide register, to have v1 undefined at the back edge.
+ const-wide/16 v0, 0x1
+ goto :header1
+
+ :pre_header2
+ goto :header2
+.end method
+
+.field public static f:Z
diff --git a/test/596-checker-dead-phi/src/Main.java b/test/596-checker-dead-phi/src/Main.java
new file mode 100644
index 0000000..5a3fffc
--- /dev/null
+++ b/test/596-checker-dead-phi/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ // Note that we don't actually enter the loops in the 'liveness'
+ // method, so this is just a sanity check that part of the code we
+ // generated for that method is correct.
+ Method m = c.getMethod("liveness", int.class);
+ Object[] arguments = { 42 };
+ System.out.println(m.invoke(null, arguments));
+ }
+}
diff --git a/test/803-no-super/expected.txt b/test/803-no-super/expected.txt
new file mode 100644
index 0000000..5036991
--- /dev/null
+++ b/test/803-no-super/expected.txt
@@ -0,0 +1,2 @@
+java.lang.ClassNotFoundException: NoSuper1
+Done!
diff --git a/test/803-no-super/info.txt b/test/803-no-super/info.txt
new file mode 100644
index 0000000..0178a44
--- /dev/null
+++ b/test/803-no-super/info.txt
@@ -0,0 +1,3 @@
+Regression test that temp (erroneous) classes don't get conflict tables created.
+
+Obviously needs to run under Dalvik or ART.
diff --git a/test/803-no-super/smali/nosuper1.smali b/test/803-no-super/smali/nosuper1.smali
new file mode 100644
index 0000000..df2eaa5
--- /dev/null
+++ b/test/803-no-super/smali/nosuper1.smali
@@ -0,0 +1,3 @@
+.class public LNoSuper1;
+
+.super LNoClass;
diff --git a/test/803-no-super/src/Main.java b/test/803-no-super/src/Main.java
new file mode 100644
index 0000000..a07e042
--- /dev/null
+++ b/test/803-no-super/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Attempt to load class with no superclass.
+ */
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try {
+ Class<?> c = Class.forName("NoSuper1");
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ System.out.println("Done!");
+ }
+}
diff --git a/test/960-default-smali/expected.txt b/test/960-default-smali/expected.txt
index 7671eed..f3db93f 100644
--- a/test/960-default-smali/expected.txt
+++ b/test/960-default-smali/expected.txt
@@ -82,3 +82,19 @@
J-interface Greeter.SayHiTwice()='Hi Hi '
J-virtual J.SayHiTwice()='Hi Hi '
End testing for type J
+Testing for type K
+K-interface Foo.bar()='foobar'
+K-virtual K.bar()='foobar'
+End testing for type K
+Testing for type L
+L-interface Foo.bar()='foobar'
+L-virtual K.bar()='foobar'
+L-virtual L.bar()='foobar'
+End testing for type L
+Testing for type M
+M-interface Foo.bar()='BAZ!'
+M-interface Fooer.bar()='BAZ!'
+M-virtual K.bar()='BAZ!'
+M-virtual L.bar()='BAZ!'
+M-virtual M.bar()='BAZ!'
+End testing for type M
diff --git a/test/960-default-smali/src/Foo.java b/test/960-default-smali/src/Foo.java
new file mode 100644
index 0000000..ed5b35f
--- /dev/null
+++ b/test/960-default-smali/src/Foo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+interface Foo {
+ public default String bar() {
+ return "foobar";
+ }
+}
diff --git a/test/960-default-smali/src/Fooer.java b/test/960-default-smali/src/Fooer.java
new file mode 100644
index 0000000..d8a5f61
--- /dev/null
+++ b/test/960-default-smali/src/Fooer.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Fooer extends Foo {
+ public String bar();
+}
diff --git a/test/960-default-smali/src/K.java b/test/960-default-smali/src/K.java
new file mode 100644
index 0000000..4426be7
--- /dev/null
+++ b/test/960-default-smali/src/K.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class K implements Foo { }
diff --git a/test/960-default-smali/src/L.java b/test/960-default-smali/src/L.java
new file mode 100644
index 0000000..c08ab72
--- /dev/null
+++ b/test/960-default-smali/src/L.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class L extends K { }
diff --git a/test/960-default-smali/src/M.java b/test/960-default-smali/src/M.java
new file mode 100644
index 0000000..affe7e9
--- /dev/null
+++ b/test/960-default-smali/src/M.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class M extends L implements Fooer {
+ public String bar() {
+ return "BAZ!";
+ }
+}
diff --git a/test/960-default-smali/src/classes.xml b/test/960-default-smali/src/classes.xml
index 0aa41f7..f3e50c5 100644
--- a/test/960-default-smali/src/classes.xml
+++ b/test/960-default-smali/src/classes.xml
@@ -81,6 +81,27 @@
<implements> </implements>
<methods> </methods>
</class>
+
+ <class name="K" super="java/lang/Object">
+ <implements>
+ <item>Foo</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="L" super="K">
+ <implements> </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="M" super="L">
+ <implements>
+ <item>Fooer</item>
+ </implements>
+ <methods>
+ <method>bar</method>
+ </methods>
+ </class>
</classes>
<interfaces>
@@ -123,5 +144,22 @@
<method type="abstract">GetPlace</method>
</methods>
</interface>
+
+ <interface name="Foo" super="java/lang/Object">
+ <implements>
+ </implements>
+ <methods>
+ <method type="default">bar</method>
+ </methods>
+ </interface>
+
+ <interface name="Fooer" super="java/lang/Object">
+ <implements>
+ <item>Foo</item>
+ </implements>
+ <methods>
+ <method type="abstract">bar</method>
+ </methods>
+ </interface>
</interfaces>
</data>
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index e547c72..d6f5d37 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -41,7 +41,9 @@
497-inlining-and-class-loader/clear_dex_cache.cc \
543-env-long-ref/env_long_ref.cc \
566-polymorphic-inlining/polymorphic_inline.cc \
- 570-checker-osr/osr.cc
+ 570-checker-osr/osr.cc \
+ 595-profile-saving/profile-saving.cc \
+ 596-app-images/app_images.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index f3cda47..ee651b5 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -563,6 +563,13 @@
TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS :=
TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
+TEST_ART_BROKEN_NPIC_RUN_TESTS := 596-app-images
+ifneq (,$(filter npictest,$(PICTEST_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ ${COMPILER_TYPES},$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+ $(IMAGE_TYPES),npictest,$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_NPIC_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
# Tests that should fail in the heap poisoning configuration with the Optimizing compiler.
# 055: Exceeds run time limits due to heap poisoning instrumentation (on ARM and ARM64 devices).
TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := \
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index d9b26bc..0cd77ab 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -77,7 +77,12 @@
* Instance.isRoot and Instance.getRootTypes.
Release History:
- 0.4 Pending
+ 0.6 Pending
+
+ 0.5 Apr 19, 2016
+ Update perflib to perflib-25.0.0 to improve processing performance.
+
+ 0.4 Feb 23, 2016
Annotate char[] objects with their string values.
Show registered native allocations for heap dumps that support it.
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index 2adec6f..d088e8c 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -25,8 +25,8 @@
import com.android.tools.perflib.heap.StackFrame;
import com.android.tools.perflib.heap.StackTrace;
import com.android.tools.perflib.captures.MemoryMappedFileBuffer;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import gnu.trove.TObjectProcedure;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -85,49 +85,59 @@
ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
for (Heap heap : mHeaps) {
- long total = 0;
- for (Instance inst : Iterables.concat(heap.getClasses(), heap.getInstances())) {
- Instance dominator = inst.getImmediateDominator();
- if (dominator != null) {
- total += inst.getSize();
+ // Use a single element array for the total to act as a reference to a
+ // long.
+ final long[] total = new long[]{0};
+ TObjectProcedure<Instance> processInstance = new TObjectProcedure<Instance>() {
+ @Override
+ public boolean execute(Instance inst) {
+ Instance dominator = inst.getImmediateDominator();
+ if (dominator != null) {
+ total[0] += inst.getSize();
- if (dominator == Snapshot.SENTINEL_ROOT) {
- mRooted.add(inst);
- }
+ if (dominator == Snapshot.SENTINEL_ROOT) {
+ mRooted.add(inst);
+ }
- // Properly label the class of a class object.
- if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
- inst.setClassId(javaLangClass.getId());
- }
+ // Properly label the class of a class object.
+ if (inst instanceof ClassObj && javaLangClass != null && inst.getClassObj() == null) {
+ inst.setClassId(javaLangClass.getId());
+ }
- // Update dominated instances.
- List<Instance> instances = mDominated.get(dominator);
- if (instances == null) {
- instances = new ArrayList<Instance>();
- mDominated.put(dominator, instances);
- }
- instances.add(inst);
+ // Update dominated instances.
+ List<Instance> instances = mDominated.get(dominator);
+ if (instances == null) {
+ instances = new ArrayList<Instance>();
+ mDominated.put(dominator, instances);
+ }
+ instances.add(inst);
- // Update sites.
- List<StackFrame> path = Collections.emptyList();
- StackTrace stack = getStack(inst);
- int stackId = getStackTraceSerialNumber(stack);
- if (stack != null) {
- StackFrame[] frames = getStackFrames(stack);
- if (frames != null && frames.length > 0) {
- path = Lists.reverse(Arrays.asList(frames));
+ // Update sites.
+ List<StackFrame> path = Collections.emptyList();
+ StackTrace stack = getStack(inst);
+ int stackId = getStackTraceSerialNumber(stack);
+ if (stack != null) {
+ StackFrame[] frames = getStackFrames(stack);
+ if (frames != null && frames.length > 0) {
+ path = Lists.reverse(Arrays.asList(frames));
+ }
+ }
+ mRootSite.add(stackId, 0, path.iterator(), inst);
+
+ // Update native allocations.
+ InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
+ if (alloc != null) {
+ mNativeAllocations.add(alloc);
}
}
- mRootSite.add(stackId, 0, path.iterator(), inst);
-
- // Update native allocations.
- InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
- if (alloc != null) {
- mNativeAllocations.add(alloc);
- }
+ return true;
}
+ };
+ for (Instance instance : heap.getClasses()) {
+ processInstance.execute(instance);
}
- mHeapSizes.put(heap, total);
+ heap.forEachInstance(processInstance);
+ mHeapSizes.put(heap, total[0]);
}
// Record the roots and their types.
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index d7b64e2..8defba2 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -244,8 +244,8 @@
if (inst instanceof ArrayInstance) {
ArrayInstance array = (ArrayInstance)inst;
- if (array.getArrayType() == Type.BYTE && inst.getHardReferences().size() == 1) {
- Instance ref = inst.getHardReferences().get(0);
+ if (array.getArrayType() == Type.BYTE && inst.getHardReverseReferences().size() == 1) {
+ Instance ref = inst.getHardReverseReferences().get(0);
ClassObj clsref = ref.getClassObj();
if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
return ref;
@@ -344,7 +344,7 @@
}
Instance referent = null;
- for (Instance ref : inst.getHardReferences()) {
+ for (Instance ref : inst.getHardReverseReferences()) {
if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
referent = InstanceUtils.getReferent(ref);
if (referent != null) {
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 06023da..4df1be5 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -160,11 +160,11 @@
private static void printReferences(
Doc doc, Query query, AhatSnapshot snapshot, Instance inst) {
doc.section("Objects with References to this Object");
- if (inst.getHardReferences().isEmpty()) {
+ if (inst.getHardReverseReferences().isEmpty()) {
doc.println(DocString.text("(none)"));
} else {
doc.table(new Column("Object"));
- List<Instance> references = inst.getHardReferences();
+ List<Instance> references = inst.getHardReverseReferences();
SubsetSelector<Instance> selector = new SubsetSelector(query, HARD_REFS_ID, references);
for (Instance ref : selector.selected()) {
doc.row(Value.render(snapshot, ref));
@@ -173,10 +173,10 @@
selector.render(doc);
}
- if (inst.getSoftReferences() != null) {
+ if (inst.getSoftReverseReferences() != null) {
doc.section("Objects with Soft References to this Object");
doc.table(new Column("Object"));
- List<Instance> references = inst.getSoftReferences();
+ List<Instance> references = inst.getSoftReverseReferences();
SubsetSelector<Instance> selector = new SubsetSelector(query, SOFT_REFS_ID, references);
for (Instance ref : selector.selected()) {
doc.row(Value.render(snapshot, ref));
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 2eb52bc..304c2a9 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -46,9 +46,14 @@
done
if [[ $mode == "host" ]]; then
- make_command="make $j_arg $showcommands build-art-host-tests $common_targets ${out_dir}/host/linux-x86/lib/libjavacoretests.so ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
+ make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
+ make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
+ make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
elif [[ $mode == "target" ]]; then
- make_command="make $j_arg $showcommands build-art-target-tests $common_targets libjavacrypto libjavacoretests linker toybox toolbox sh ${out_dir}/host/linux-x86/bin/adb libstdc++"
+ make_command="make $j_arg $showcommands build-art-target-tests $common_targets"
+ make_command+=" libjavacrypto libjavacoretests linker toybox toolbox sh"
+ make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
+ make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
fi
echo "Executing $make_command"
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 38b6ea6..dd2cc31 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -243,48 +243,6 @@
"org.apache.harmony.tests.java.util.prefs.FilePreferencesImplTest#testPutGet"]
},
{
- description: "libnativehelper_compat_libc++ loading issue",
- result: EXEC_FAILED,
- modes: [device],
- names: ["dalvik.system.JniTest#testGetSuperclass",
- "dalvik.system.JniTest#testPassingBooleans",
- "dalvik.system.JniTest#testPassingBytes",
- "dalvik.system.JniTest#testPassingChars",
- "dalvik.system.JniTest#testPassingClass",
- "dalvik.system.JniTest#testPassingDoubles",
- "dalvik.system.JniTest#testPassingFloats",
- "dalvik.system.JniTest#testPassingInts",
- "dalvik.system.JniTest#testPassingLongs",
- "dalvik.system.JniTest#testPassingObjectReferences",
- "dalvik.system.JniTest#testPassingShorts",
- "dalvik.system.JniTest#testPassingThis",
- "libcore.util.NativeAllocationRegistryTest#testBadSize",
- "libcore.util.NativeAllocationRegistryTest#testEarlyFree",
- "libcore.util.NativeAllocationRegistryTest#testNativeAllocationAllocatorAndNoSharedRegistry",
- "libcore.util.NativeAllocationRegistryTest#testNativeAllocationAllocatorAndSharedRegistry",
- "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry",
- "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndSharedRegistry",
- "libcore.util.NativeAllocationRegistryTest#testNullArguments"]
-},
-{
- description: "libnativehelper_compat_libc++.so not found by dlopen on ARM64",
- result: EXEC_FAILED,
- modes: [device],
- bug: 28082914,
- names: ["libcore.java.lang.ThreadTest#testContextClassLoaderIsInherited",
- "libcore.java.lang.ThreadTest#testContextClassLoaderIsNotNull",
- "libcore.java.lang.ThreadTest#testGetAllStackTracesIncludesAllGroups",
- "libcore.java.lang.ThreadTest#testGetStackTrace",
- "libcore.java.lang.ThreadTest#testJavaContextClassLoader",
- "libcore.java.lang.ThreadTest#testLeakingStartedThreads",
- "libcore.java.lang.ThreadTest#testLeakingUnstartedThreads",
- "libcore.java.lang.ThreadTest#testNativeThreadNames",
- "libcore.java.lang.ThreadTest#testThreadInterrupted",
- "libcore.java.lang.ThreadTest#testThreadSleep",
- "libcore.java.lang.ThreadTest#testThreadSleepIllegalArguments",
- "libcore.java.lang.ThreadTest#testThreadWakeup"]
-},
-{
description: "Only work with --mode=activity",
result: EXEC_FAILED,
names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ]
@@ -295,5 +253,11 @@
names: ["jsr166.CollectionTest#testEmptyMeansEmpty",
"jsr166.Collection8Test#testForEach",
"jsr166.Collection8Test#testForEachConcurrentStressTest"]
+},
+{
+ description: "Unclear why this started to fail",
+ result: EXEC_FAILED,
+ bug: 28574453,
+ names: [ "org.apache.harmony.tests.javax.security.cert.X509CertificateTest#testVerifyPublicKey" ]
}
]
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
new file mode 100644
index 0000000..4b01796
--- /dev/null
+++ b/tools/public.libraries.buildbot.txt
@@ -0,0 +1,8 @@
+libart.so
+libartd.so
+libbacktrace.so
+libc.so
+libc++.so
+libdl.so
+libm.so
+libnativehelper.so