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
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 036da2e..b1acf5e 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -447,7 +447,7 @@
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 d055b37..0e73bd9 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -341,7 +341,7 @@
Compiler::Kind compiler_kind,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- bool image, std::unordered_set<std::string>* image_classes,
+ bool boot_image, std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
size_t thread_count, bool dump_stats, bool dump_passes,
@@ -361,7 +361,7 @@
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 @@
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 @@
}
bool CompilerDriver::IsImageClass(const char* descriptor) const {
- if (!IsImage()) {
+ if (!IsBootImage()) {
// NOTE: Currently unreachable, all callers check IsImage().
return false;
} else {
@@ -790,7 +790,7 @@
}
bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
- if (kRestrictCompilationFiltersToImage && !IsImage()) {
+ if (kRestrictCompilationFiltersToImage && !IsBootImage()) {
return true;
}
@@ -801,7 +801,7 @@
}
bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const {
- if (kRestrictCompilationFiltersToImage && !IsImage()) {
+ if (kRestrictCompilationFiltersToImage && !IsBootImage()) {
return true;
}
@@ -889,7 +889,7 @@
// Make a list of descriptors for classes to include in the image
void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
CHECK(timings != nullptr);
- if (!IsImage()) {
+ if (!IsBootImage()) {
return;
}
@@ -1118,7 +1118,7 @@
};
void CompilerDriver::UpdateImageClasses(TimingLogger* timings) {
- if (IsImage()) {
+ if (IsBootImage()) {
TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
Runtime* runtime = Runtime::Current();
@@ -1145,7 +1145,7 @@
// Having the klass reference here implies that the klass is already loaded.
return true;
}
- if (!IsImage()) {
+ if (!IsBootImage()) {
// Assume loaded only if klass is in the boot image. App classes cannot be assumed
// loaded because we don't even know what class loader will be used to load them.
bool class_in_image = runtime->GetHeap()->FindSpaceFromObject(klass, false)->IsImageSpace();
@@ -1157,7 +1157,7 @@
}
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 @@
// 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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 4ed4dc6..15b6bba 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -92,7 +92,7 @@
Compiler::Kind compiler_kind,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- bool image, std::unordered_set<std::string>* image_classes,
+ bool boot_image, std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
size_t thread_count, bool dump_stats, bool dump_passes,
@@ -156,8 +156,8 @@
}
// Are we compiling and creating an image file?
- bool IsImage() const {
- return image_;
+ bool IsBootImage() const {
+ return boot_image_;
}
const std::unordered_set<std::string>* GetImageClasses() const {
@@ -637,7 +637,7 @@
// 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 fd6cd82..a38e1f5 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -64,8 +64,10 @@
ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
const uintptr_t requested_image_base = ART_BASE_ADDRESS;
- std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_, requested_image_base,
- /*compile_pic*/false));
+ std::unique_ptr<ImageWriter> 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 @@
t.NewTiming("WriteElf");
SafeMap<std::string, std::string> 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 af2a4f9..0c85323 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -20,6 +20,7 @@
#include <memory>
#include <numeric>
+#include <unordered_set>
#include <vector>
#include "art_field-inl.h"
@@ -72,6 +73,27 @@
// 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 @@
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 @@
Runtime::Current()->GetOatFileManager().RegisterOatFile(
std::unique_ptr<const OatFile>(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 @@
for (jobject weak_root : class_linker->GetDexCaches()) {
mirror::DexCache* dex_cache =
down_cast<mirror::DexCache*>(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::AddDexCacheArrayRelocation(void* array, size_t offset) {
if (array != nullptr) {
+ DCHECK(!IsInBootImage(array));
native_object_relocations_.emplace(
array,
NativeObjectRelocation { offset, kNativeObjectRelocationTypeDexCacheArray });
@@ -344,8 +375,8 @@
auto* method = arr->GetElementPtrSize<ArtMethod*>(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 @@
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<mirror::Object>(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<mirror::Object>));
+ }
+ }
+ 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 @@
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<std::string> non_image_classes_;
+ std::unordered_set<mirror::Class*> 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 @@
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 @@
}
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 @@
// 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 @@
}
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 @@
// 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::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) {
ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(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, 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<mirror::DexCache*>(self->DecodeJObject(weak_root));
+ dex_cache_count += IsInBootImage(dex_cache) ? 0u : 1u;
+ }
}
Handle<ObjectArray<Object>> dex_caches(
- hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(),
- dex_cache_count)));
+ hs.NewHandle(ObjectArray<Object>::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<mirror::DexCache*>(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<mirror::DexCache*>(self->DecodeJObject(weak_root));
- dex_caches->Set<false>(i, dex_cache);
- ++i;
+ mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(weak_root));
+ if (!IsInBootImage(dex_cache)) {
+ dex_caches->Set<false>(i, dex_cache);
+ ++i;
+ }
}
}
@@ -761,6 +863,10 @@
// 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 @@
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 @@
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 @@
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::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::UnbinObjectsIntoOffsetCallback(mirror::Object* obj, void* arg) {
ImageWriter* writer = reinterpret_cast<ImageWriter*>(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 @@
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<ArtMethod>::ComputeSize(
0, ArtMethod::Size(target_ptr_size_), method_alignment);
@@ -940,7 +1055,10 @@
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 @@
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 @@
auto* image_header = reinterpret_cast<ImageHeader*>(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<ArtMethod*>(image_begin_ + it->second.offset);
- image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(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<ArtMethod*>(image_begin_ + it->second.offset);
+ }
+ image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method);
}
// Write the intern table into the image.
const ImageSection& intern_table_section = image_header->GetImageSection(
@@ -1183,8 +1304,8 @@
dst->SetClass(GetImageAddress(arr->GetClass()));
auto* dest_array = down_cast<mirror::PointerArray*>(dst);
for (size_t i = 0, count = num_elements; i < count; ++i) {
- auto* elem = arr->GetElementPtrSize<void*>(i, target_ptr_size_);
- if (elem != nullptr) {
+ void* elem = arr->GetElementPtrSize<void*>(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::CopyAndFixupObject(Object* obj) {
+ if (IsInBootImage(obj)) {
+ return;
+ }
size_t offset = GetImageOffset(obj);
auto* dst = reinterpret_cast<Object*>(image_->Begin() + offset);
DCHECK_LT(offset, image_end_);
@@ -1282,18 +1406,19 @@
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 <typename T>
T* ImageWriter::NativeLocationInImage(T* obj) {
- if (obj == nullptr) {
- return nullptr;
- }
- return reinterpret_cast<T*>(image_begin_ + NativeOffsetInImage(obj));
+ return (obj == nullptr || IsInBootImage(obj))
+ ? obj
+ : reinterpret_cast<T*>(image_begin_ + NativeOffsetInImage(obj));
}
void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
@@ -1306,18 +1431,22 @@
// 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<ArtMethod*>(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<ArtMethod*>(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 @@
reinterpret_cast<ArtMethod**>(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 @@
ArtField** copy_fields = reinterpret_cast<ArtField**>(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<const uint8_t*>(header.GetQuickGenericJniTrampoline());
+ case kOatAddressInterpreterToInterpreterBridge:
+ return static_cast<const uint8_t*>(header.GetInterpreterToInterpreterBridge());
+ case kOatAddressInterpreterToCompiledCodeBridge:
+ return static_cast<const uint8_t*>(header.GetInterpreterToCompiledCodeBridge());
+ case kOatAddressJNIDlsymLookup:
+ return static_cast<const uint8_t*>(header.GetJniDlsymLookup());
+ case kOatAddressQuickIMTConflictTrampoline:
+ return static_cast<const uint8_t*>(header.GetQuickImtConflictTrampoline());
+ case kOatAddressQuickResolutionTrampoline:
+ return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline());
+ case kOatAddressQuickToInterpreterBridge:
+ return static_cast<const uint8_t*>(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 @@
// 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 @@
// 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 @@
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<size_t>(Runtime::kLastCalleeSaveType); ++i) {
@@ -1535,7 +1699,7 @@
// 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 @@
// 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 7a2febc..120de97 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<uint8_t*>(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<uint8_t*>(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 @@
template <typename T>
T* GetImageAddress(T* object) const SHARED_REQUIRES(Locks::mutator_lock_) {
- return object == nullptr ? nullptr :
- reinterpret_cast<T*>(image_begin_ + GetImageOffset(object));
+ return (object == nullptr || IsInBootImage(object))
+ ? object
+ : reinterpret_cast<T*>(image_begin_ + GetImageOffset(object));
}
ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -150,6 +166,19 @@
};
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<uint32_t>(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 @@
return reinterpret_cast<mirror::Object*>(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 @@
}
// 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 @@
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 @@
template <typename T>
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 @@
std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> 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 @@
uint64_t dirty_methods_;
uint64_t clean_methods_;
+ // Prune class memoization table.
+ std::unordered_map<mirror::Class*, bool> 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 ea3cb66..83d7834 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -111,6 +111,7 @@
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 c7b8884..3f2271e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -65,10 +65,12 @@
int32_t image_patch_delta,
const CompilerDriver* compiler,
ImageWriter* image_writer,
+ bool compiling_boot_image,
TimingLogger* timings,
SafeMap<std::string, std::string>* 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 @@
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 @@
}
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 @@
}
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_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 @@
}
uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
- if (writer_->HasImage()) {
+ if (writer_->HasBootImage()) {
auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>(
patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_;
@@ -868,7 +873,7 @@
void PatchObjectAddress(std::vector<uint8_t>* 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 @@
void PatchMethodAddress(std::vector<uint8_t>* 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 @@
void PatchCodeAddress(std::vector<uint8_t>* 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 @@
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 @@
} while (false)
VISIT(InitCodeMethodVisitor);
- if (compiler_driver_->IsImage()) {
+ if (compiler_driver_->IsBootImage()) {
VISIT(InitImageMethodVisitor);
}
@@ -1408,7 +1413,7 @@
}
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 f2fe048..7027434 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -93,6 +93,7 @@
int32_t image_patch_delta,
const CompilerDriver* compiler,
ImageWriter* image_writer,
+ bool compiling_boot_image,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store);
@@ -103,6 +104,10 @@
return image_writer_ != nullptr;
}
+ bool HasBootImage() const {
+ return compiling_boot_image_;
+ }
+
const OatHeader& GetOatHeader() const {
return *oat_header_;
}
@@ -279,6 +284,7 @@
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<const DexFile*>* dex_files_;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 2653807..92ed58c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1355,9 +1355,20 @@
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<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()),
+ kPageSize);
+ VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(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 @@
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 @@
}
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 da70456..a6eb5b2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -847,8 +847,8 @@
hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
- space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
- AsObjectArray<mirror::Class>()));
+ space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
+ AsObjectArray<mirror::Class>()));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// Special case of setting up the String class early so that we can test arbitrary objects
@@ -857,7 +857,7 @@
mirror::Class* java_lang_Object = GetClassRoot(kJavaLangObject);
java_lang_Object->SetObjectSize(sizeof(mirror::Object));
- Runtime::Current()->SetSentinel(Runtime::Current()->GetHeap()->AllocObject<true>(self,
+ Runtime::Current()->SetSentinel(heap->AllocObject<true>(self,
java_lang_Object,
java_lang_Object->GetObjectSize(),
VoidFunctor()));
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 18867fd..e6e154e 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -367,6 +367,7 @@
--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}"