From c033474cfbfe1e963c07fa5c38aed02e35ed6f91 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 2 Nov 2015 10:30:20 -0800 Subject: [PATCH] Add basic image writer support for app images Needed to handle references from app image -> boot image. Generate app images for tests to enable some testing. Bug: 22858531 Change-Id: I1af98b6c4dfcb3a147fb5b0dea64aa4946c7ce57 --- compiler/dex/quick/arm64/call_arm64.cc | 2 +- compiler/driver/compiler_driver.cc | 38 ++-- compiler/driver/compiler_driver.h | 8 +- compiler/image_test.cc | 17 +- compiler/image_writer.cc | 360 ++++++++++++++++++++++++--------- compiler/image_writer.h | 96 ++++++--- compiler/oat_test.cc | 1 + compiler/oat_writer.cc | 31 +-- compiler/oat_writer.h | 6 + dex2oat/dex2oat.cc | 23 ++- runtime/class_linker.cc | 6 +- test/etc/run-test-jar | 1 + 12 files changed, 420 insertions(+), 169 deletions(-) diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 036da2e2b..b1acf5e69 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -447,7 +447,7 @@ void Arm64Mir2Lir::GenSpecialExitForSuspend() { static bool Arm64UseRelativeCall(CompilationUnit* cu, const MethodReference& target_method) { // Emit relative calls anywhere in the image or within a dex file otherwise. - return cu->compiler_driver->IsImage() || cu->dex_file == target_method.dex_file; + return cu->compiler_driver->IsBootImage() || cu->dex_file == target_method.dex_file; } /* diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index d055b37ea..0e73bd926 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -341,7 +341,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool image, std::unordered_set* image_classes, + bool boot_image, std::unordered_set* image_classes, std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, size_t thread_count, bool dump_stats, bool dump_passes, @@ -361,7 +361,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, compiled_methods_lock_("compiled method lock"), compiled_methods_(MethodTable::key_compare()), non_relative_linker_patch_count_(0u), - image_(image), + boot_image_(boot_image), image_classes_(image_classes), classes_to_compile_(compiled_classes), methods_to_compile_(compiled_methods), @@ -383,7 +383,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, compiler_->Init(); - CHECK_EQ(image_, image_classes_.get() != nullptr); + CHECK_EQ(boot_image_, image_classes_.get() != nullptr); // Read the profile file if one is provided. if (!profile_file.empty()) { @@ -781,7 +781,7 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vectorGetHeap()->FindSpaceFromObject(klass, false)->IsImageSpace(); @@ -1157,7 +1157,7 @@ bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) { } bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { - if (IsImage() && + if (IsBootImage() && IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) { { ScopedObjectAccess soa(Thread::Current()); @@ -1183,7 +1183,7 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, // See also Compiler::ResolveDexFile bool result = false; - if (IsImage()) { + if (IsBootImage()) { // We resolve all const-string strings when building for the image. ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); @@ -1300,7 +1300,7 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i if (compiling_boot) { // boot -> boot class pointers. // True if the class is in the image at boot compiling time. - const bool is_image_class = IsImage() && IsImageClass( + const bool is_image_class = IsBootImage() && IsImageClass( dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_)); // True if pc relative load works. if (is_image_class && support_boot_image_fixup) { @@ -1548,7 +1548,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType } if (!use_dex_cache && force_relocations) { bool is_in_image; - if (IsImage()) { + if (IsBootImage()) { is_in_image = IsImageClass(method->GetDeclaringClassDescriptor()); } else { is_in_image = instruction_set_ != kX86 && instruction_set_ != kX86_64 && @@ -2019,7 +2019,7 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files, thread_pool); - if (IsImage()) { + if (IsBootImage()) { // For images we resolve all types, such as array, whereas for applications just those with // classdefs are resolved by ResolveClassFieldsAndMethods. TimingLogger::ScopedTiming t("Resolve Types", timings); @@ -2101,8 +2101,8 @@ class VerifyClassVisitor : public CompilationVisitor { // It is *very* problematic if there are verification errors in the boot classpath. For example, // we rely on things working OK without verification when the decryption dialog is brought up. // So abort in a debug build if we find this violated. - DCHECK(!manager_->GetCompiler()->IsImage() || klass->IsVerified()) << "Boot classpath class " - << PrettyClass(klass.Get()) << " failed to fully verify."; + DCHECK(!manager_->GetCompiler()->IsBootImage() || klass->IsVerified()) + << "Boot classpath class " << PrettyClass(klass.Get()) << " failed to fully verify."; } soa.Self()->AssertNoPendingException(); } @@ -2222,7 +2222,7 @@ class InitializeClassVisitor : public CompilationVisitor { if (!klass->IsInitialized()) { // We need to initialize static fields, we only do this for image classes that aren't // marked with the $NoPreloadHolder (which implies this should not be initialized early). - bool can_init_static_fields = manager_->GetCompiler()->IsImage() && + bool can_init_static_fields = manager_->GetCompiler()->IsBootImage() && manager_->GetCompiler()->IsImageClass(descriptor) && !StringPiece(descriptor).ends_with("$NoPreloadHolder;"); if (can_init_static_fields) { @@ -2286,7 +2286,7 @@ void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, dex_files, thread_pool); size_t thread_count; - if (IsImage()) { + if (IsBootImage()) { // TODO: remove this when transactional mode supports multithreading. thread_count = 1U; } else { @@ -2304,7 +2304,7 @@ void CompilerDriver::InitializeClasses(jobject class_loader, CHECK(dex_file != nullptr); InitializeClasses(class_loader, *dex_file, dex_files, thread_pool, timings); } - if (IsImage()) { + if (IsBootImage()) { // Prune garbage objects created during aborted transactions. Runtime::Current()->GetHeap()->CollectGarbage(true); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 4ed4dc60d..15b6bba27 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -92,7 +92,7 @@ class CompilerDriver { Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool image, std::unordered_set* image_classes, + bool boot_image, std::unordered_set* image_classes, std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, size_t thread_count, bool dump_stats, bool dump_passes, @@ -156,8 +156,8 @@ class CompilerDriver { } // Are we compiling and creating an image file? - bool IsImage() const { - return image_; + bool IsBootImage() const { + return boot_image_; } const std::unordered_set* GetImageClasses() const { @@ -637,7 +637,7 @@ class CompilerDriver { // in the .oat_patches ELF section if requested in the compiler options. size_t non_relative_linker_patch_count_ GUARDED_BY(compiled_methods_lock_); - const bool image_; + const bool boot_image_; // If image_ is true, specifies the classes that will be included in // the image. Note if image_classes_ is null, all classes are diff --git a/compiler/image_test.cc b/compiler/image_test.cc index fd6cd82f7..a38e1f54c 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -64,8 +64,10 @@ TEST_F(ImageTest, WriteRead) { ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str())); const uintptr_t requested_image_base = ART_BASE_ADDRESS; - std::unique_ptr writer(new ImageWriter(*compiler_driver_, requested_image_base, - /*compile_pic*/false)); + std::unique_ptr writer(new ImageWriter(*compiler_driver_, + requested_image_base, + /*compile_pic*/false, + /*compile_app_image*/false)); // TODO: compile_pic should be a test argument. { { @@ -81,8 +83,15 @@ TEST_F(ImageTest, WriteRead) { t.NewTiming("WriteElf"); SafeMap key_value_store; - OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), - writer.get(), &timings, &key_value_store); + OatWriter oat_writer(class_linker->GetBootClassPath(), + 0, + 0, + 0, + compiler_driver_.get(), + writer.get(), + /*compiling_boot_image*/true, + &timings, + &key_value_store); bool success = writer->PrepareImageAddressSpace() && compiler_driver_->WriteElf(GetTestAndroidRoot(), !kIsTargetBuild, diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index af2a4f942..0c8532380 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -20,6 +20,7 @@ #include #include +#include #include #include "art_field-inl.h" @@ -72,6 +73,27 @@ namespace art { // Separate objects into multiple bins to optimize dirty memory use. static constexpr bool kBinObjects = true; +// Return true if an object is already in an image space. +bool ImageWriter::IsInBootImage(const void* obj) const { + if (!compile_app_image_) { + DCHECK(boot_image_space_ == nullptr); + return false; + } + const uint8_t* image_begin = boot_image_space_->Begin(); + // Real image end including ArtMethods and ArtField sections. + const uint8_t* image_end = image_begin + boot_image_space_->GetImageHeader().GetImageSize(); + return image_begin <= obj && obj < image_end; +} + +bool ImageWriter::IsInBootOatFile(const void* ptr) const { + if (!compile_app_image_) { + DCHECK(boot_image_space_ == nullptr); + return false; + } + const ImageHeader& image_header = boot_image_space_->GetImageHeader(); + return image_header.GetOatFileBegin() <= ptr && ptr < image_header.GetOatFileEnd(); +} + static void CheckNoDexObjectsCallback(Object* obj, void* arg ATTRIBUTE_UNUSED) SHARED_REQUIRES(Locks::mutator_lock_) { Class* klass = obj->GetClass(); @@ -85,12 +107,20 @@ static void CheckNoDexObjects() { bool ImageWriter::PrepareImageAddressSpace() { target_ptr_size_ = InstructionSetPointerSize(compiler_driver_.GetInstructionSet()); + gc::Heap* const heap = Runtime::Current()->GetHeap(); + // Cache boot image space. + for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { + if (space->IsImageSpace()) { + CHECK(compile_app_image_); + CHECK(boot_image_space_ == nullptr) << "Multiple image spaces"; + boot_image_space_ = space->AsImageSpace(); + } + } { ScopedObjectAccess soa(Thread::Current()); PruneNonImageClasses(); // Remove junk ComputeLazyFieldsForImageClasses(); // Add useful information } - gc::Heap* heap = Runtime::Current()->GetHeap(); heap->CollectGarbage(false); // Remove garbage. // Dex caches must not have their dex fields set in the image. These are memory buffers of mapped @@ -144,21 +174,21 @@ bool ImageWriter::Write(int image_fd, Runtime::Current()->GetOatFileManager().RegisterOatFile( std::unique_ptr(oat_file_)); - interpreter_to_interpreter_bridge_offset_ = - oat_file_->GetOatHeader().GetInterpreterToInterpreterBridgeOffset(); - interpreter_to_compiled_code_bridge_offset_ = - oat_file_->GetOatHeader().GetInterpreterToCompiledCodeBridgeOffset(); - - jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset(); - - quick_generic_jni_trampoline_offset_ = - oat_file_->GetOatHeader().GetQuickGenericJniTrampolineOffset(); - quick_imt_conflict_trampoline_offset_ = - oat_file_->GetOatHeader().GetQuickImtConflictTrampolineOffset(); - quick_resolution_trampoline_offset_ = - oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset(); - quick_to_interpreter_bridge_offset_ = - oat_file_->GetOatHeader().GetQuickToInterpreterBridgeOffset(); + const OatHeader& oat_header = oat_file_->GetOatHeader(); + oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = + oat_header.GetInterpreterToInterpreterBridgeOffset(); + oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = + oat_header.GetInterpreterToCompiledCodeBridgeOffset(); + oat_address_offsets_[kOatAddressJNIDlsymLookup] = + oat_header.GetJniDlsymLookupOffset(); + oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = + oat_header.GetQuickGenericJniTrampolineOffset(); + oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = + oat_header.GetQuickImtConflictTrampolineOffset(); + oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = + oat_header.GetQuickResolutionTrampolineOffset(); + oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = + oat_header.GetQuickToInterpreterBridgeOffset(); size_t oat_loaded_size = 0; size_t oat_data_offset = 0; @@ -307,7 +337,7 @@ void ImageWriter::PrepareDexCacheArraySlots() { for (jobject weak_root : class_linker->GetDexCaches()) { mirror::DexCache* dex_cache = down_cast(self->DecodeJObject(weak_root)); - if (dex_cache == nullptr) { + if (dex_cache == nullptr || IsInBootImage(dex_cache)) { continue; } const DexFile* dex_file = dex_cache->GetDexFile(); @@ -331,6 +361,7 @@ void ImageWriter::PrepareDexCacheArraySlots() { void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset) { if (array != nullptr) { + DCHECK(!IsInBootImage(array)); native_object_relocations_.emplace( array, NativeObjectRelocation { offset, kNativeObjectRelocationTypeDexCacheArray }); @@ -344,8 +375,8 @@ void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) { auto* method = arr->GetElementPtrSize(i, target_ptr_size_); if (method != nullptr && !method->IsRuntimeMethod()) { auto* klass = method->GetDeclaringClass(); - CHECK(klass == nullptr || IsImageClass(klass)) << PrettyClass(klass) - << " should be an image class"; + CHECK(klass == nullptr || KeepClass(klass)) + << PrettyClass(klass) << " should be a kept class"; } } } @@ -539,10 +570,66 @@ void ImageWriter::ComputeLazyFieldsForImageClasses() { class_linker->VisitClassesWithoutClassesLock(&visitor); } -bool ImageWriter::IsImageClass(Class* klass) { +static bool IsBootClassLoaderClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { + return klass->GetClassLoader() == nullptr; +} + +bool ImageWriter::IsBootClassLoaderNonImageClass(mirror::Class* klass) { + return IsBootClassLoaderClass(klass) && !IsInBootImage(klass); +} + +bool ImageWriter::ContainsBootClassLoaderNonImageClass(mirror::Class* klass) { + if (klass == nullptr) { + return false; + } + auto found = prune_class_memo_.find(klass); + if (found != prune_class_memo_.end()) { + // Already computed, return the found value. + return found->second; + } + // Place holder value to prevent infinite recursion. + prune_class_memo_.emplace(klass, false); + bool result = IsBootClassLoaderNonImageClass(klass); + if (!result) { + // Check interfaces since these wont be visited through VisitReferences.) + mirror::IfTable* if_table = klass->GetIfTable(); + for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) { + result = result || ContainsBootClassLoaderNonImageClass(if_table->GetInterface(i)); + } + } + // Check static fields and their classes. + size_t num_static_fields = klass->NumReferenceStaticFields(); + if (num_static_fields != 0 && klass->IsResolved()) { + // Presumably GC can happen when we are cross compiling, it should not cause performance + // problems to do pointer size logic. + MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset( + Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + for (size_t i = 0u; i < num_static_fields; ++i) { + mirror::Object* ref = klass->GetFieldObject(field_offset); + if (ref != nullptr) { + if (ref->IsClass()) { + result = result || ContainsBootClassLoaderNonImageClass(ref->AsClass()); + } + result = result || ContainsBootClassLoaderNonImageClass(ref->GetClass()); + } + field_offset = MemberOffset(field_offset.Uint32Value() + + sizeof(mirror::HeapReference)); + } + } + result = result || ContainsBootClassLoaderNonImageClass(klass->GetSuperClass()); + prune_class_memo_[klass] = result; + return result; +} + +bool ImageWriter::KeepClass(Class* klass) { if (klass == nullptr) { return false; } + if (compile_app_image_) { + // For app images, we need to prune boot loader classes that are not in the boot image since + // these may have already been loaded when the app image is loaded. + return !ContainsBootClassLoaderNonImageClass(klass); + } std::string temp; return compiler_driver_.IsImageClass(klass->GetDescriptor(&temp)); } @@ -552,21 +639,17 @@ class NonImageClassesVisitor : public ClassVisitor { explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {} bool Visit(Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { - if (!image_writer_->IsImageClass(klass)) { - std::string temp; - non_image_classes_.insert(klass->GetDescriptor(&temp)); + if (!image_writer_->KeepClass(klass)) { + classes_to_prune_.insert(klass); } return true; } - std::set non_image_classes_; + std::unordered_set classes_to_prune_; ImageWriter* const image_writer_; }; void ImageWriter::PruneNonImageClasses() { - if (compiler_driver_.GetImageClasses() == nullptr) { - return; - } Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); Thread* self = Thread::Current(); @@ -576,8 +659,14 @@ void ImageWriter::PruneNonImageClasses() { class_linker->VisitClasses(&visitor); // Remove the undesired classes from the class roots. - for (const std::string& it : visitor.non_image_classes_) { - bool result = class_linker->RemoveClass(it.c_str(), nullptr); + for (mirror::Class* klass : visitor.classes_to_prune_) { + std::string temp; + const char* name = klass->GetDescriptor(&temp); + VLOG(compiler) << "Pruning class " << name; + if (!compile_app_image_) { + DCHECK(IsBootClassLoaderClass(klass)); + } + bool result = class_linker->RemoveClass(name, klass->GetClassLoader()); DCHECK(result); } @@ -594,7 +683,7 @@ void ImageWriter::PruneNonImageClasses() { } for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { Class* klass = dex_cache->GetResolvedType(i); - if (klass != nullptr && !IsImageClass(klass)) { + if (klass != nullptr && !KeepClass(klass)) { dex_cache->SetResolvedType(i, nullptr); } } @@ -607,7 +696,7 @@ void ImageWriter::PruneNonImageClasses() { // Miranda methods may be held live by a class which was not an image class but have a // declaring class which is an image class. Set it to the resolution method to be safe and // prevent dangling pointers. - if (method->IsMiranda() || !IsImageClass(declaring_class)) { + if (method->IsMiranda() || !KeepClass(declaring_class)) { mirror::DexCache::SetElementPtrSize(resolved_methods, i, resolution_method, @@ -621,7 +710,7 @@ void ImageWriter::PruneNonImageClasses() { } for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { ArtField* field = dex_cache->GetResolvedField(i, target_ptr_size_); - if (field != nullptr && !IsImageClass(field->GetDeclaringClass())) { + if (field != nullptr && !KeepClass(field->GetDeclaringClass())) { dex_cache->SetResolvedField(i, nullptr, target_ptr_size_); } } @@ -632,6 +721,9 @@ void ImageWriter::PruneNonImageClasses() { // Drop the array class cache in the ClassLinker, as these are roots holding those classes live. class_linker->DropFindArrayClassCache(); + + // Clear to save RAM. + prune_class_memo_.clear(); } void ImageWriter::CheckNonImageClassesRemoved() { @@ -643,13 +735,13 @@ void ImageWriter::CheckNonImageClassesRemoved() { void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { ImageWriter* image_writer = reinterpret_cast(arg); - if (obj->IsClass()) { + if (obj->IsClass() && !image_writer->IsInBootImage(obj)) { Class* klass = obj->AsClass(); - if (!image_writer->IsImageClass(klass)) { + if (!image_writer->KeepClass(klass)) { image_writer->DumpImageClasses(); std::string temp; - CHECK(image_writer->IsImageClass(klass)) << klass->GetDescriptor(&temp) - << " " << PrettyDescriptor(klass); + CHECK(image_writer->KeepClass(klass)) << klass->GetDescriptor(&temp) + << " " << PrettyDescriptor(klass); } } } @@ -703,25 +795,35 @@ ObjectArray* ImageWriter::CreateImageRoots() const { // ObjectArray, we lock the dex lock twice, first to get the number // of dex caches first and then lock it again to copy the dex // caches. We check that the number of dex caches does not change. - size_t dex_cache_count; + size_t dex_cache_count = 0; { ReaderMutexLock mu(self, *class_linker->DexLock()); - dex_cache_count = class_linker->GetDexCacheCount(); + // Count number of dex caches not in the boot image. + for (jobject weak_root : class_linker->GetDexCaches()) { + mirror::DexCache* dex_cache = down_cast(self->DecodeJObject(weak_root)); + dex_cache_count += IsInBootImage(dex_cache) ? 0u : 1u; + } } Handle> dex_caches( - hs.NewHandle(ObjectArray::Alloc(self, object_array_class.Get(), - dex_cache_count))); + hs.NewHandle(ObjectArray::Alloc(self, object_array_class.Get(), dex_cache_count))); CHECK(dex_caches.Get() != nullptr) << "Failed to allocate a dex cache array."; { ReaderMutexLock mu(self, *class_linker->DexLock()); - CHECK_EQ(dex_cache_count, class_linker->GetDexCacheCount()) - << "The number of dex caches changed."; + size_t non_image_dex_caches = 0; + // Re-count number of non image dex caches. + for (jobject weak_root : class_linker->GetDexCaches()) { + mirror::DexCache* dex_cache = down_cast(self->DecodeJObject(weak_root)); + non_image_dex_caches += IsInBootImage(dex_cache) ? 0u : 1u; + } + CHECK_EQ(dex_cache_count, non_image_dex_caches) + << "The number of non-image dex caches changed."; size_t i = 0; for (jobject weak_root : class_linker->GetDexCaches()) { - mirror::DexCache* dex_cache = - down_cast(self->DecodeJObject(weak_root)); - dex_caches->Set(i, dex_cache); - ++i; + mirror::DexCache* dex_cache = down_cast(self->DecodeJObject(weak_root)); + if (!IsInBootImage(dex_cache)) { + dex_caches->Set(i, dex_cache); + ++i; + } } } @@ -761,6 +863,10 @@ void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) // For an unvisited object, visit it then all its children found via fields. void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { + if (IsInBootImage(obj)) { + // Object is in the image, don't need to fix it up. + return; + } // Use our own visitor routine (instead of GC visitor) to get better locality between // an object and its fields if (!IsImageBinSlotAssigned(obj)) { @@ -797,6 +903,7 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields << " already forwarded"; size_t& offset = bin_slot_sizes_[kBinArtField]; + DCHECK(!IsInBootImage(cur_fields)); native_object_relocations_.emplace( cur_fields, NativeObjectRelocation { offset, kNativeObjectRelocationTypeArtFieldArray }); @@ -808,6 +915,7 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { auto it2 = native_object_relocations_.find(field); CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i << " already assigned " << PrettyField(field) << " static=" << field->IsStatic(); + DCHECK(!IsInBootImage(field)); native_object_relocations_.emplace( field, NativeObjectRelocation {offset, kNativeObjectRelocationTypeArtField }); offset += sizeof(ArtField); @@ -843,6 +951,7 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { CHECK(it == native_object_relocations_.end()) << "Method array " << array << " already forwarded"; size_t& offset = bin_slot_sizes_[bin_type]; + DCHECK(!IsInBootImage(array)); native_object_relocations_.emplace(array, NativeObjectRelocation { offset, any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty : kNativeObjectRelocationTypeArtMethodArrayClean }); @@ -867,6 +976,7 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { } void ImageWriter::AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type) { + DCHECK(!IsInBootImage(method)); auto it = native_object_relocations_.find(method); CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned " << PrettyMethod(method); @@ -884,10 +994,13 @@ void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) { void ImageWriter::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) { ImageWriter* writer = reinterpret_cast(arg); DCHECK(writer != nullptr); - writer->UnbinObjectsIntoOffset(obj); + if (!writer->IsInBootImage(obj)) { + writer->UnbinObjectsIntoOffset(obj); + } } void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) { + DCHECK(!IsInBootImage(obj)); CHECK(obj != nullptr); // We know the bin slot, and the total bin sizes for all objects by now, @@ -925,13 +1038,15 @@ void ImageWriter::CalculateNewObjectOffsets() { image_methods_[ImageHeader::kRefsAndArgsSaveMethod] = runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs); - // Add room for fake length prefixed array. + // 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()); size_t& offset = bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)]; - native_object_relocations_.emplace(&image_method_array_, - NativeObjectRelocation { offset, image_method_type }); + if (!compile_app_image_) { + native_object_relocations_.emplace(&image_method_array_, + NativeObjectRelocation { offset, image_method_type }); + } size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); const size_t array_size = LengthPrefixedArray::ComputeSize( 0, ArtMethod::Size(target_ptr_size_), method_alignment); @@ -940,7 +1055,10 @@ void ImageWriter::CalculateNewObjectOffsets() { for (auto* m : image_methods_) { CHECK(m != nullptr); CHECK(m->IsRuntimeMethod()); - AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean); + DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; + if (!IsInBootImage(m)) { + AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean); + } } // Calculate size of the dex cache arrays slot and prepare offsets. PrepareDexCacheArraySlots(); @@ -1090,6 +1208,7 @@ void ImageWriter::CopyAndFixupNativeData() { NativeObjectRelocation& relocation = pair.second; auto* dest = image_->Begin() + relocation.offset; DCHECK_GE(dest, image_->Begin() + image_end_); + DCHECK(!IsInBootImage(pair.first)); switch (relocation.type) { case kNativeObjectRelocationTypeArtField: { memcpy(dest, pair.first, sizeof(ArtField)); @@ -1126,16 +1245,18 @@ void ImageWriter::CopyAndFixupNativeData() { auto* image_header = reinterpret_cast(image_->Begin()); const ImageSection& methods_section = image_header->GetMethodsSection(); for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) { - auto* m = image_methods_[i]; - CHECK(m != nullptr); - auto it = native_object_relocations_.find(m); - CHECK(it != native_object_relocations_.end()) << "No fowarding for " << PrettyMethod(m); - NativeObjectRelocation& relocation = it->second; - CHECK(methods_section.Contains(relocation.offset)) << relocation.offset << " not in " - << methods_section; - CHECK(relocation.IsArtMethodRelocation()) << relocation.type; - auto* dest = reinterpret_cast(image_begin_ + it->second.offset); - image_header->SetImageMethod(static_cast(i), dest); + ArtMethod* method = image_methods_[i]; + CHECK(method != nullptr); + if (!IsInBootImage(method)) { + auto it = native_object_relocations_.find(method); + CHECK(it != native_object_relocations_.end()) << "No fowarding 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(image_begin_ + it->second.offset); + } + image_header->SetImageMethod(static_cast(i), method); } // Write the intern table into the image. const ImageSection& intern_table_section = image_header->GetImageSection( @@ -1183,8 +1304,8 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a dst->SetClass(GetImageAddress(arr->GetClass())); auto* dest_array = down_cast(dst); for (size_t i = 0, count = num_elements; i < count; ++i) { - auto* elem = arr->GetElementPtrSize(i, target_ptr_size_); - if (elem != nullptr) { + void* elem = arr->GetElementPtrSize(i, target_ptr_size_); + if (elem != nullptr && !IsInBootImage(elem)) { auto it = native_object_relocations_.find(elem); if (UNLIKELY(it == native_object_relocations_.end())) { if (it->second.IsArtMethodRelocation()) { @@ -1209,6 +1330,9 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a } void ImageWriter::CopyAndFixupObject(Object* obj) { + if (IsInBootImage(obj)) { + return; + } size_t offset = GetImageOffset(obj); auto* dst = reinterpret_cast(image_->Begin() + offset); DCHECK_LT(offset, image_end_); @@ -1282,18 +1406,19 @@ class FixupClassVisitor FINAL : public FixupVisitor { uintptr_t ImageWriter::NativeOffsetInImage(void* obj) { DCHECK(obj != nullptr); + DCHECK(!IsInBootImage(obj)); auto it = native_object_relocations_.find(obj); - CHECK(it != native_object_relocations_.end()) << obj; + CHECK(it != native_object_relocations_.end()) << obj << " spaces " + << Runtime::Current()->GetHeap()->DumpSpaces(); const NativeObjectRelocation& relocation = it->second; return relocation.offset; } template T* ImageWriter::NativeLocationInImage(T* obj) { - if (obj == nullptr) { - return nullptr; - } - return reinterpret_cast(image_begin_ + NativeOffsetInImage(obj)); + return (obj == nullptr || IsInBootImage(obj)) + ? obj + : reinterpret_cast(image_begin_ + NativeOffsetInImage(obj)); } void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) { @@ -1306,18 +1431,22 @@ void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) { // Update dex cache strings. copy->SetDexCacheStrings(NativeLocationInImage(orig->GetDexCacheStrings())); // Fix up embedded tables. - if (orig->ShouldHaveEmbeddedImtAndVTable()) { - for (int32_t i = 0; i < orig->GetEmbeddedVTableLength(); ++i) { - auto it = native_object_relocations_.find(orig->GetEmbeddedVTableEntry(i, target_ptr_size_)); - CHECK(it != native_object_relocations_.end()) << PrettyClass(orig); - copy->SetEmbeddedVTableEntryUnchecked( - i, reinterpret_cast(image_begin_ + it->second.offset), target_ptr_size_); - } - for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { - auto it = native_object_relocations_.find(orig->GetEmbeddedImTableEntry(i, target_ptr_size_)); - CHECK(it != native_object_relocations_.end()) << PrettyClass(orig); - copy->SetEmbeddedImTableEntry( - i, reinterpret_cast(image_begin_ + it->second.offset), target_ptr_size_); + if (!orig->IsTemp()) { + // TODO: Why do we have temp classes in some cases? + if (orig->ShouldHaveEmbeddedImtAndVTable()) { + for (int32_t i = 0; i < orig->GetEmbeddedVTableLength(); ++i) { + ArtMethod* orig_method = orig->GetEmbeddedVTableEntry(i, target_ptr_size_); + copy->SetEmbeddedVTableEntryUnchecked( + i, + NativeLocationInImage(orig_method), + target_ptr_size_); + } + for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { + copy->SetEmbeddedImTableEntry( + i, + NativeLocationInImage(orig->GetEmbeddedImTableEntry(i, target_ptr_size_)), + target_ptr_size_); + } } } FixupClassVisitor visitor(this, copy); @@ -1419,7 +1548,7 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, reinterpret_cast(image_->Begin() + copy_methods_offset); for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) { ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_); - ArtMethod* copy = NativeLocationInImage(orig); + ArtMethod* copy = IsInBootImage(orig) ? orig : NativeLocationInImage(orig); mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_); } } @@ -1432,15 +1561,51 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, ArtField** copy_fields = reinterpret_cast(image_->Begin() + copy_fields_offset); for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) { ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_); - ArtField* copy = NativeLocationInImage(orig); + ArtField* copy = IsInBootImage(orig) ? orig : NativeLocationInImage(orig); mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_); } } } +const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { + DCHECK_LT(type, kOatAddressCount); + // If we are compiling an app image, we need to use the stubs of the boot image. + if (compile_app_image_) { + // Use the current image pointers. + gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); + DCHECK(image_space != nullptr); + const OatFile* oat_file = image_space->GetOatFile(); + CHECK(oat_file != nullptr); + const OatHeader& header = oat_file->GetOatHeader(); + switch (type) { + // TODO: We could maybe clean this up if we stored them in an array in the oat header. + case kOatAddressQuickGenericJNITrampoline: + return static_cast(header.GetQuickGenericJniTrampoline()); + case kOatAddressInterpreterToInterpreterBridge: + return static_cast(header.GetInterpreterToInterpreterBridge()); + case kOatAddressInterpreterToCompiledCodeBridge: + return static_cast(header.GetInterpreterToCompiledCodeBridge()); + case kOatAddressJNIDlsymLookup: + return static_cast(header.GetJniDlsymLookup()); + case kOatAddressQuickIMTConflictTrampoline: + return static_cast(header.GetQuickImtConflictTrampoline()); + case kOatAddressQuickResolutionTrampoline: + return static_cast(header.GetQuickResolutionTrampoline()); + case kOatAddressQuickToInterpreterBridge: + return static_cast(header.GetQuickToInterpreterBridge()); + default: + UNREACHABLE(); + } + } + return GetOatAddressForOffset(oat_address_offsets_[type]); +} + const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_interpreted) { - DCHECK(!method->IsResolutionMethod() && !method->IsImtConflictMethod() && - !method->IsImtUnimplementedMethod() && !method->IsAbstract()) << PrettyMethod(method); + DCHECK(!method->IsResolutionMethod()) << PrettyMethod(method); + DCHECK(!method->IsImtConflictMethod()) << PrettyMethod(method); + DCHECK(!method->IsImtUnimplementedMethod()) << PrettyMethod(method); + DCHECK(!method->IsAbstract()) << PrettyMethod(method); + DCHECK(!IsInBootImage(method)) << PrettyMethod(method); // Use original code if it exists. Otherwise, set the code pointer to the resolution // trampoline. @@ -1448,27 +1613,26 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_inter // Quick entrypoint: uint32_t quick_oat_code_offset = PointerToLowMemUInt32( method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_)); - const uint8_t* quick_code = GetOatAddress(quick_oat_code_offset); + const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset); *quick_is_interpreted = false; if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) { // We have code for a non-static or initialized method, just use the code. - DCHECK_GE(quick_code, oat_data_begin_); } else if (quick_code == nullptr && method->IsNative() && (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) { // Non-static or initialized native method missing compiled code, use generic JNI version. - quick_code = GetOatAddress(quick_generic_jni_trampoline_offset_); - DCHECK_GE(quick_code, oat_data_begin_); + quick_code = GetOatAddress(kOatAddressQuickGenericJNITrampoline); } else if (quick_code == nullptr && !method->IsNative()) { // We don't have code at all for a non-native method, use the interpreter. - quick_code = GetOatAddress(quick_to_interpreter_bridge_offset_); + quick_code = GetOatAddress(kOatAddressQuickToInterpreterBridge); *quick_is_interpreted = true; - DCHECK_GE(quick_code, oat_data_begin_); } else { CHECK(!method->GetDeclaringClass()->IsInitialized()); // We have code for a static method, but need to go through the resolution stub for class // initialization. - quick_code = GetOatAddress(quick_resolution_trampoline_offset_); + quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline); + } + if (!IsInBootOatFile(quick_code)) { DCHECK_GE(quick_code, oat_data_begin_); } return quick_code; @@ -1479,16 +1643,16 @@ const uint8_t* ImageWriter::GetQuickEntryPoint(ArtMethod* method) { // The resolution method has a special trampoline to call. Runtime* runtime = Runtime::Current(); if (UNLIKELY(method == runtime->GetResolutionMethod())) { - return GetOatAddress(quick_resolution_trampoline_offset_); + return GetOatAddress(kOatAddressQuickResolutionTrampoline); } else if (UNLIKELY(method == runtime->GetImtConflictMethod() || method == runtime->GetImtUnimplementedMethod())) { - return GetOatAddress(quick_imt_conflict_trampoline_offset_); + return GetOatAddress(kOatAddressQuickIMTConflictTrampoline); } 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 // use results in an AbstractMethodError. We use the interpreter to achieve this. if (UNLIKELY(method->IsAbstract())) { - return GetOatAddress(quick_to_interpreter_bridge_offset_); + return GetOatAddress(kOatAddressQuickToInterpreterBridge); } else { bool quick_is_interpreted; return GetQuickCode(method, &quick_is_interpreted); @@ -1513,11 +1677,11 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) { Runtime* runtime = Runtime::Current(); if (UNLIKELY(orig == runtime->GetResolutionMethod())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(quick_resolution_trampoline_offset_), target_ptr_size_); + GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_); } else if (UNLIKELY(orig == runtime->GetImtConflictMethod() || orig == runtime->GetImtUnimplementedMethod())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(quick_imt_conflict_trampoline_offset_), target_ptr_size_); + GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_); } else if (UNLIKELY(orig->IsRuntimeMethod())) { bool found_one = false; for (size_t i = 0; i < static_cast(Runtime::kLastCalleeSaveType); ++i) { @@ -1535,7 +1699,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) { // use results in an AbstractMethodError. We use the interpreter to achieve this. if (UNLIKELY(orig->IsAbstract())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(quick_to_interpreter_bridge_offset_), target_ptr_size_); + GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_); } else { bool quick_is_interpreted; const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted); @@ -1546,7 +1710,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) { // The native method's pointer is set to a stub to lookup via dlsym. // Note this is not the code_ pointer, that is handled above. copy->SetEntryPointFromJniPtrSize( - GetOatAddress(jni_dlsym_lookup_offset_), target_ptr_size_); + GetOatAddress(kOatAddressJNIDlsymLookup), target_ptr_size_); } } } diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 7a2febcea..120de9762 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -40,27 +40,42 @@ #include "utils.h" namespace art { +namespace gc { +namespace space { +class ImageSpace; +} // namespace space +} // namespace gc static constexpr int kInvalidImageFd = -1; // Write a Space built during compilation for use during execution. class ImageWriter FINAL { public: - ImageWriter(const CompilerDriver& compiler_driver, uintptr_t image_begin, - bool compile_pic) - : compiler_driver_(compiler_driver), image_begin_(reinterpret_cast(image_begin)), - image_end_(0), image_objects_offset_begin_(0), image_roots_address_(0), oat_file_(nullptr), - oat_data_begin_(nullptr), interpreter_to_interpreter_bridge_offset_(0), - interpreter_to_compiled_code_bridge_offset_(0), jni_dlsym_lookup_offset_(0), - quick_generic_jni_trampoline_offset_(0), - quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0), - quick_to_interpreter_bridge_offset_(0), compile_pic_(compile_pic), + ImageWriter(const CompilerDriver& compiler_driver, + uintptr_t image_begin, + bool compile_pic, + bool compile_app_image) + : compiler_driver_(compiler_driver), + image_begin_(reinterpret_cast(image_begin)), + image_end_(0), + image_objects_offset_begin_(0), + image_roots_address_(0), + oat_file_(nullptr), + oat_data_begin_(nullptr), + compile_pic_(compile_pic), + compile_app_image_(compile_app_image), + boot_image_space_(nullptr), target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), - bin_slot_sizes_(), bin_slot_offsets_(), bin_slot_count_(), - intern_table_bytes_(0u), image_method_array_(ImageHeader::kImageMethodsCount), - dirty_methods_(0u), clean_methods_(0u) { + bin_slot_sizes_(), + bin_slot_offsets_(), + bin_slot_count_(), + intern_table_bytes_(0u), + image_method_array_(ImageHeader::kImageMethodsCount), + dirty_methods_(0u), + clean_methods_(0u) { CHECK_NE(image_begin, 0U); - std::fill(image_methods_, image_methods_ + arraysize(image_methods_), nullptr); + std::fill_n(image_methods_, arraysize(image_methods_), nullptr); + std::fill_n(oat_address_offsets_, arraysize(oat_address_offsets_), 0); } ~ImageWriter() { @@ -74,8 +89,9 @@ class ImageWriter FINAL { template T* GetImageAddress(T* object) const SHARED_REQUIRES(Locks::mutator_lock_) { - return object == nullptr ? nullptr : - reinterpret_cast(image_begin_ + GetImageOffset(object)); + return (object == nullptr || IsInBootImage(object)) + ? object + : reinterpret_cast(image_begin_ + GetImageOffset(object)); } ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); @@ -150,6 +166,19 @@ class ImageWriter FINAL { }; friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type); + enum OatAddress { + kOatAddressInterpreterToInterpreterBridge, + kOatAddressInterpreterToCompiledCodeBridge, + kOatAddressJNIDlsymLookup, + kOatAddressQuickGenericJNITrampoline, + kOatAddressQuickIMTConflictTrampoline, + kOatAddressQuickResolutionTrampoline, + kOatAddressQuickToInterpreterBridge, + // Number of elements in the enum. + kOatAddressCount, + }; + friend std::ostream& operator<<(std::ostream& stream, const OatAddress& oat_address); + static constexpr size_t kBinBits = MinimumBitsToStore(kBinMirrorCount - 1); // uint32 = typeof(lockword_) // Subtract read barrier bits since we want these to remain 0, or else it may result in DCHECK @@ -215,7 +244,10 @@ class ImageWriter FINAL { return reinterpret_cast(dst); } - const uint8_t* GetOatAddress(uint32_t offset) const { + // Returns the address in the boot image if we are compiling the app image. + const uint8_t* GetOatAddress(OatAddress type) const; + + const uint8_t* GetOatAddressForOffset(uint32_t offset) const { // With Quick, code is within the OatFile, as there are all in one // .o ELF object. DCHECK_LE(offset, oat_file_->Size()); @@ -224,7 +256,7 @@ class ImageWriter FINAL { } // Returns true if the class was in the original requested image classes list. - bool IsImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); + bool KeepClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); // Debug aid that list of requested image classes. void DumpImageClasses(); @@ -299,6 +331,11 @@ class ImageWriter FINAL { void AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type) SHARED_REQUIRES(Locks::mutator_lock_); + bool IsBootClassLoaderNonImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); + + bool ContainsBootClassLoaderNonImageClass(mirror::Class* klass) + SHARED_REQUIRES(Locks::mutator_lock_); + static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type); uintptr_t NativeOffsetInImage(void* obj); @@ -306,6 +343,13 @@ class ImageWriter FINAL { template T* NativeLocationInImage(T* obj); + // Return true of obj is inside of the boot image space. This may only return true if we are + // compiling an app image. + bool IsInBootImage(const void* obj) const; + + // Return true if ptr is within the boot oat file. + bool IsInBootOatFile(const void* ptr) const; + const CompilerDriver& compiler_driver_; // Beginning target image address for the output image. @@ -344,14 +388,14 @@ class ImageWriter FINAL { std::unique_ptr image_bitmap_; // Offset from oat_data_begin_ to the stubs. - uint32_t interpreter_to_interpreter_bridge_offset_; - uint32_t interpreter_to_compiled_code_bridge_offset_; - uint32_t jni_dlsym_lookup_offset_; - uint32_t quick_generic_jni_trampoline_offset_; - uint32_t quick_imt_conflict_trampoline_offset_; - uint32_t quick_resolution_trampoline_offset_; - uint32_t quick_to_interpreter_bridge_offset_; + uint32_t oat_address_offsets_[kOatAddressCount]; + + // Boolean flags. const bool compile_pic_; + const bool compile_app_image_; + + // Boot image space for fast lookups. + gc::space::ImageSpace* boot_image_space_; // Size of pointers on the target architecture. size_t target_ptr_size_; @@ -388,6 +432,10 @@ class ImageWriter FINAL { uint64_t dirty_methods_; uint64_t clean_methods_; + // Prune class memoization table. + std::unordered_map prune_class_memo_; + + friend class ContainsBootClassLoaderNonImageClassVisitor; friend class FixupClassVisitor; friend class FixupRootVisitor; friend class FixupVisitor; diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index ea3cb667e..83d783408 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -111,6 +111,7 @@ TEST_F(OatTest, WriteRead) { 0, compiler_driver_.get(), nullptr, + /*compiling_boot_image*/false, &timings, &key_value_store); bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(), diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c7b888421..3f2271ef1 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -65,10 +65,12 @@ OatWriter::OatWriter(const std::vector& dex_files, int32_t image_patch_delta, const CompilerDriver* compiler, ImageWriter* image_writer, + bool compiling_boot_image, TimingLogger* timings, SafeMap* key_value_store) : compiler_driver_(compiler), image_writer_(image_writer), + compiling_boot_image_(compiling_boot_image), dex_files_(&dex_files), size_(0u), bss_size_(0u), @@ -113,7 +115,9 @@ OatWriter::OatWriter(const std::vector& dex_files, size_oat_lookup_table_(0), method_offset_map_() { CHECK(key_value_store != nullptr); - + if (compiling_boot_image) { + CHECK(image_writer != nullptr); + } InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features, @@ -154,7 +158,7 @@ OatWriter::OatWriter(const std::vector& dex_files, } size_ = offset; - if (!HasImage()) { + if (!HasBootImage()) { // Allocate space for app dex cache arrays in the .bss section. size_t bss_start = RoundUp(size_, kPageSize); size_t pointer_size = GetInstructionSetPointerSize(instruction_set); @@ -167,9 +171,10 @@ OatWriter::OatWriter(const std::vector& dex_files, } CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); - CHECK_EQ(compiler->IsImage(), image_writer_ != nullptr); - CHECK_EQ(compiler->IsImage(), - key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end()); + if (compiling_boot_image_) { + CHECK_EQ(image_writer_ != nullptr, + key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end()); + } CHECK_ALIGNED(image_patch_delta_, kPageSize); } @@ -672,7 +677,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { class_linker_(Runtime::Current()->GetClassLinker()), dex_cache_(nullptr) { patched_code_.reserve(16 * KB); - if (writer_->HasImage()) { + if (writer_->HasBootImage()) { // If we're creating the image, the address space must be ready so that we can apply patches. CHECK(writer_->image_writer_->IsImageAddressSpaceReady()); } @@ -855,7 +860,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) { - if (writer_->HasImage()) { + if (writer_->HasBootImage()) { auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress( patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_; @@ -868,7 +873,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { void PatchObjectAddress(std::vector* code, uint32_t offset, mirror::Object* object) SHARED_REQUIRES(Locks::mutator_lock_) { - if (writer_->HasImage()) { + if (writer_->HasBootImage()) { object = writer_->image_writer_->GetImageAddress(object); } else { // NOTE: We're using linker patches for app->boot references when the image can @@ -888,7 +893,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { void PatchMethodAddress(std::vector* code, uint32_t offset, ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { - if (writer_->HasImage()) { + if (writer_->HasBootImage()) { method = writer_->image_writer_->GetImageMethodAddress(method); } else if (kIsDebugBuild) { // NOTE: We're using linker patches for app->boot references when the image can @@ -911,7 +916,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { void PatchCodeAddress(std::vector* code, uint32_t offset, uint32_t target_offset) SHARED_REQUIRES(Locks::mutator_lock_) { uint32_t address = target_offset; - if (writer_->HasImage()) { + if (writer_->HasBootImage()) { address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() + writer_->oat_data_offset_ + target_offset); } @@ -1123,7 +1128,7 @@ size_t OatWriter::InitOatCode(size_t offset) { offset = RoundUp(offset, kPageSize); oat_header_->SetExecutableOffset(offset); size_executable_offset_alignment_ = offset - old_offset; - if (compiler_driver_->IsImage()) { + if (compiler_driver_->IsBootImage()) { CHECK_EQ(image_patch_delta_, 0); InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); @@ -1164,7 +1169,7 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } while (false) VISIT(InitCodeMethodVisitor); - if (compiler_driver_->IsImage()) { + if (compiler_driver_->IsBootImage()) { VISIT(InitImageMethodVisitor); } @@ -1408,7 +1413,7 @@ size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t } size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset) { - if (compiler_driver_->IsImage()) { + if (compiler_driver_->IsBootImage()) { InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); #define DO_TRAMPOLINE(field) \ diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index f2fe04817..7027434cc 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -93,6 +93,7 @@ class OatWriter { int32_t image_patch_delta, const CompilerDriver* compiler, ImageWriter* image_writer, + bool compiling_boot_image, TimingLogger* timings, SafeMap* key_value_store); @@ -103,6 +104,10 @@ class OatWriter { return image_writer_ != nullptr; } + bool HasBootImage() const { + return compiling_boot_image_; + } + const OatHeader& GetOatHeader() const { return *oat_header_; } @@ -279,6 +284,7 @@ class OatWriter { const CompilerDriver* const compiler_driver_; ImageWriter* const image_writer_; + const bool compiling_boot_image_; // note OatFile does not take ownership of the DexFiles const std::vector* dex_files_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 265380736..92ed58cb8 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1355,9 +1355,20 @@ class Dex2Oat FINAL { uint32_t image_file_location_oat_checksum = 0; uintptr_t image_file_location_oat_data_begin = 0; int32_t image_patch_delta = 0; + + if (app_image_ && image_base_ == 0) { + gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); + image_base_ = RoundUp( + reinterpret_cast(image_space->GetImageHeader().GetOatFileEnd()), + kPageSize); + VLOG(compiler) << "App image base=" << reinterpret_cast(image_base_); + } + if (IsImage()) { PrepareImageWriter(image_base_); - } else { + } + + if (!IsBootImage()) { TimingLogger::ScopedTiming t3("Loading image checksum", timings_); gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace(); image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum(); @@ -1371,11 +1382,13 @@ class Dex2Oat FINAL { key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); } - oat_writer.reset(new OatWriter(dex_files_, image_file_location_oat_checksum, + oat_writer.reset(new OatWriter(dex_files_, + image_file_location_oat_checksum, image_file_location_oat_data_begin, image_patch_delta, driver_.get(), image_writer_.get(), + IsBootImage(), timings_, key_value_store_.get())); } @@ -1591,7 +1604,11 @@ class Dex2Oat FINAL { } void PrepareImageWriter(uintptr_t image_base) { - image_writer_.reset(new ImageWriter(*driver_, image_base, compiler_options_->GetCompilePic())); + DCHECK(IsImage()); + image_writer_.reset(new ImageWriter(*driver_, + image_base, + compiler_options_->GetCompilePic(), + IsAppImage())); } // Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index da7045636..a6eb5b2b3 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -847,8 +847,8 @@ void ClassLinker::InitFromImage() { hs.NewHandle(dex_caches_object->AsObjectArray())); Handle> class_roots(hs.NewHandle( - space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> - AsObjectArray())); + space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> + AsObjectArray())); class_roots_ = GcRoot>(class_roots.Get()); // Special case of setting up the String class early so that we can test arbitrary objects @@ -857,7 +857,7 @@ void ClassLinker::InitFromImage() { mirror::Class* java_lang_Object = GetClassRoot(kJavaLangObject); java_lang_Object->SetObjectSize(sizeof(mirror::Object)); - Runtime::Current()->SetSentinel(Runtime::Current()->GetHeap()->AllocObject(self, + Runtime::Current()->SetSentinel(heap->AllocObject(self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 18867fd03..e6e154e43 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -367,6 +367,7 @@ if [ "$PREBUILD" = "y" ]; then --boot-image=${BOOT_IMAGE} \ --dex-file=$DEX_LOCATION/$TEST_NAME.jar \ --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") \ + --app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex.art | cut -d/ -f 2- | sed "s:/:@:g") \ --instruction-set=$ISA" if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}" -- 2.11.0