Dex2oat support for multiple oat file and image file outputs.
Multiple changes to dex2oat and the runtime to support a --multi-image
option. This generates a separate oat file and image file output for
each dex file input.
Change-Id: Ie1d6f0b8afa8aed5790065b8c2eb177990c60129
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index f1b2965..78297ae 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -56,30 +56,31 @@
uintptr_t image_begin,
bool compile_pic,
bool compile_app_image,
- ImageHeader::StorageMode image_storage_mode)
+ ImageHeader::StorageMode image_storage_mode,
+ const std::vector<const char*> oat_filenames,
+ const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map)
: compiler_driver_(compiler_driver),
- image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
- image_end_(0),
+ global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
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),
class_table_bytes_(0u),
- image_storage_mode_(image_storage_mode) {
+ image_storage_mode_(image_storage_mode),
+ dex_file_oat_filename_map_(dex_file_oat_filename_map),
+ oat_filenames_(oat_filenames),
+ default_oat_filename_(oat_filenames[0]) {
CHECK_NE(image_begin, 0U);
+ for (const char* oat_filename : oat_filenames) {
+ image_info_map_.emplace(oat_filename, ImageInfo());
+ }
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
- std::fill_n(oat_address_offsets_, arraysize(oat_address_offsets_), 0);
}
~ImageWriter() {
@@ -88,14 +89,25 @@
bool PrepareImageAddressSpace();
bool IsImageAddressSpaceReady() const {
- return image_roots_address_ != 0u;
+ bool ready = !image_info_map_.empty();
+ for (auto& pair : image_info_map_) {
+ const ImageInfo& image_info = pair.second;
+ if (image_info.image_roots_address_ == 0u) {
+ return false;
+ }
+ }
+ return ready;
}
template <typename T>
T* GetImageAddress(T* object) const SHARED_REQUIRES(Locks::mutator_lock_) {
- return (object == nullptr || IsInBootImage(object))
- ? object
- : reinterpret_cast<T*>(image_begin_ + GetImageOffset(object));
+ if (object == nullptr || IsInBootImage(object)) {
+ return object;
+ } else {
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object));
+ }
}
ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
@@ -103,26 +115,36 @@
template <typename PtrType>
PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset)
const SHARED_REQUIRES(Locks::mutator_lock_) {
- auto it = dex_cache_array_starts_.find(dex_file);
- DCHECK(it != dex_cache_array_starts_.end());
+ auto oat_it = dex_file_oat_filename_map_.find(dex_file);
+ DCHECK(oat_it != dex_file_oat_filename_map_.end());
+ const ImageInfo& image_info = GetConstImageInfo(oat_it->second);
+ auto it = image_info.dex_cache_array_starts_.find(dex_file);
+ DCHECK(it != image_info.dex_cache_array_starts_.end());
return reinterpret_cast<PtrType>(
- image_begin_ + bin_slot_offsets_[kBinDexCacheArray] + it->second + offset);
+ image_info.image_begin_ + image_info.bin_slot_offsets_[kBinDexCacheArray] +
+ it->second + offset);
}
- uint8_t* GetOatFileBegin() const;
+ uint8_t* GetOatFileBegin(const char* oat_filename) const;
// If image_fd is not kInvalidImageFd, then we use that for the file. Otherwise we open
- // image_filename.
+ // the names in image_filenames.
bool Write(int image_fd,
- const std::string& image_filename,
- const std::string& oat_filename,
- const std::string& oat_location)
+ const std::vector<const char*>& image_filenames,
+ const std::vector<const char*>& oat_filenames)
REQUIRES(!Locks::mutator_lock_);
- uintptr_t GetOatDataBegin() {
- return reinterpret_cast<uintptr_t>(oat_data_begin_);
+ uintptr_t GetOatDataBegin(const char* oat_filename) {
+ return reinterpret_cast<uintptr_t>(GetImageInfo(oat_filename).oat_data_begin_);
}
+ const char* GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Update the oat size for the given oat file. This will make the oat_offset for the next oat
+ // file valid.
+ void UpdateOatFile(const char* oat_filename);
+
private:
bool AllocMemory();
@@ -214,6 +236,58 @@
const uint32_t lockword_;
};
+ struct ImageInfo {
+ explicit ImageInfo()
+ : image_begin_(nullptr),
+ image_end_(RoundUp(sizeof(ImageHeader), kObjectAlignment)),
+ image_roots_address_(0),
+ image_offset_(0),
+ image_size_(0),
+ oat_offset_(0),
+ bin_slot_sizes_(),
+ bin_slot_offsets_(),
+ bin_slot_count_() {}
+
+ std::unique_ptr<MemMap> image_; // Memory mapped for generating the image.
+
+ // Target begin of this image. Notes: It is not valid to write here, this is the address
+ // of the target image, not necessarily where image_ is mapped. The address is only valid
+ // after layouting (otherwise null).
+ uint8_t* image_begin_;
+
+ size_t image_end_; // Offset to the free space in image_, initially size of image header.
+ uint32_t image_roots_address_; // The image roots address in the image.
+ size_t image_offset_; // Offset of this image from the start of the first image.
+
+ // Image size is the *address space* covered by this image. As the live bitmap is aligned
+ // to the page size, the live bitmap will cover more address space than necessary. But live
+ // bitmaps may not overlap, so an image has a "shadow," which is accounted for in the size.
+ // The next image may only start at image_begin_ + image_size_ (which is guaranteed to be
+ // page-aligned).
+ size_t image_size_;
+
+ // Oat data.
+ size_t oat_offset_; // Offset of the oat file for this image from start of oat files. This is
+ // valid when the previous oat file has been written.
+ uint8_t* oat_data_begin_; // Start of oatdata in the corresponding oat file. This is
+ // valid when the images have been layed out.
+ size_t oat_size_; // Size of the corresponding oat data.
+
+ // Image bitmap which lets us know where the objects inside of the image reside.
+ std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
+
+ // The start offsets of the dex cache arrays.
+ SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
+
+ // Offset from oat_data_begin_ to the stubs.
+ uint32_t oat_address_offsets_[kOatAddressCount];
+
+ // Bin slot tracking for dirty object packing.
+ size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin.
+ size_t bin_slot_offsets_[kBinSize]; // Number of bytes in previous bins.
+ size_t bin_slot_count_[kBinSize]; // Number of objects in a bin.
+ };
+
// We use the lock word to store the offset of the object in the image.
void AssignImageOffset(mirror::Object* object, BinSlot bin_slot)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -233,7 +307,8 @@
SHARED_REQUIRES(Locks::mutator_lock_);
BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
- void AddDexCacheArrayRelocation(void* array, size_t offset) SHARED_REQUIRES(Locks::mutator_lock_);
+ void AddDexCacheArrayRelocation(void* array, size_t offset, mirror::DexCache* dex_cache)
+ SHARED_REQUIRES(Locks::mutator_lock_);
void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_REQUIRES(Locks::mutator_lock_);
static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
@@ -244,19 +319,21 @@
mirror::Object* GetLocalAddress(mirror::Object* object) const
SHARED_REQUIRES(Locks::mutator_lock_) {
size_t offset = GetImageOffset(object);
- uint8_t* dst = image_->Begin() + offset;
+ const char* oat_filename = GetOatFilename(object);
+ const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ uint8_t* dst = image_info.image_->Begin() + offset;
return reinterpret_cast<mirror::Object*>(dst);
}
// 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 {
+ const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const {
// With Quick, code is within the OatFile, as there are all in one
- // .o ELF object.
- DCHECK_LE(offset, oat_file_->Size());
- DCHECK(oat_data_begin_ != nullptr);
- return offset == 0u ? nullptr : oat_data_begin_ + offset;
+ // .o ELF object. But interpret it as signed.
+ DCHECK_LE(static_cast<int32_t>(offset), static_cast<int32_t>(image_info.oat_size_));
+ DCHECK(image_info.oat_data_begin_ != nullptr);
+ return offset == 0u ? nullptr : image_info.oat_data_begin_ + static_cast<int32_t>(offset);
}
// Returns true if the class was in the original requested image classes list.
@@ -282,7 +359,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset)
SHARED_REQUIRES(Locks::mutator_lock_);
- mirror::ObjectArray<mirror::Object>* CreateImageRoots() const
+ mirror::ObjectArray<mirror::Object>* CreateImageRoots(const char* oat_filename) const
SHARED_REQUIRES(Locks::mutator_lock_);
void CalculateObjectBinSlots(mirror::Object* obj)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -304,7 +381,7 @@
static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
SHARED_REQUIRES(Locks::mutator_lock_);
void CopyAndFixupObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
- void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy)
+ void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info)
SHARED_REQUIRES(Locks::mutator_lock_);
void FixupClass(mirror::Class* orig, mirror::Class* copy)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -319,23 +396,24 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Get quick code for non-resolution/imt_conflict/abstract method.
- const uint8_t* GetQuickCode(ArtMethod* method, bool* quick_is_interpreted)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- const uint8_t* GetQuickEntryPoint(ArtMethod* method)
+ const uint8_t* GetQuickCode(ArtMethod* method,
+ const ImageInfo& image_info,
+ bool* quick_is_interpreted)
SHARED_REQUIRES(Locks::mutator_lock_);
// Patches references in OatFile to expect runtime addresses.
void SetOatChecksumFromElfFile(File* elf_file);
// Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
- size_t GetBinSizeSum(Bin up_to = kBinSize) const;
+ size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const;
// Return true if a method is likely to be dirtied at runtime.
bool WillMethodBeDirty(ArtMethod* m) const SHARED_REQUIRES(Locks::mutator_lock_);
// Assign the offset for an ArtMethod.
- void AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type)
+ void AssignMethodOffset(ArtMethod* method,
+ NativeObjectRelocationType type,
+ const char* oat_filename)
SHARED_REQUIRES(Locks::mutator_lock_);
// Return true if klass is loaded by the boot class loader but not in the boot image.
@@ -359,11 +437,11 @@
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
- T* NativeLocationInImage(T* obj);
+ T* NativeLocationInImage(T* obj, const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_);
// Location of where the temporary copy of the object currently is.
template <typename T>
- T* NativeCopyLocation(T* obj);
+ T* NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) SHARED_REQUIRES(Locks::mutator_lock_);
// Return true of obj is inside of the boot image space. This may only return true if we are
// compiling an app image.
@@ -372,46 +450,35 @@
// Return true if ptr is within the boot oat file.
bool IsInBootOatFile(const void* ptr) const;
+ const char* GetOatFilename(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+ const char* GetDefaultOatFilename() const {
+ return default_oat_filename_;
+ }
+
+ ImageInfo& GetImageInfo(const char* oat_filename);
+ const ImageInfo& GetConstImageInfo(const char* oat_filename) const;
+ const ImageInfo& GetImageInfo(size_t index) const;
+
const CompilerDriver& compiler_driver_;
- // Beginning target image address for the output image.
- uint8_t* image_begin_;
-
- // Offset to the free space in image_.
- size_t image_end_;
+ // Beginning target image address for the first image.
+ uint8_t* global_image_begin_;
// Offset from image_begin_ to where the first object is in image_.
size_t image_objects_offset_begin_;
- // The image roots address in the image.
- uint32_t image_roots_address_;
-
// oat file with code for this image
OatFile* oat_file_;
- // Memory mapped for generating the image.
- std::unique_ptr<MemMap> image_;
-
// Pointer arrays that need to be updated. Since these are only some int and long arrays, we need
// to keep track. These include vtable arrays, iftable arrays, and dex caches.
std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_;
- // The start offsets of the dex cache arrays.
- SafeMap<const DexFile*, size_t> dex_cache_array_starts_;
-
// Saved hash codes. We use these to restore lockwords which were temporarily used to have
// forwarding addresses as well as copying over hash codes.
std::unordered_map<mirror::Object*, uint32_t> saved_hashcode_map_;
- // Beginning target oat address for the pointers from the output image to its oat file.
- const uint8_t* oat_data_begin_;
-
- // Image bitmap which lets us know where the objects inside of the image reside.
- std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
-
- // Offset from oat_data_begin_ to the stubs.
- uint32_t oat_address_offsets_[kOatAddressCount];
-
// Boolean flags.
const bool compile_pic_;
const bool compile_app_image_;
@@ -422,10 +489,8 @@
// Size of pointers on the target architecture.
size_t target_ptr_size_;
- // Bin slot tracking for dirty object packing
- size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin
- size_t bin_slot_offsets_[kBinSize]; // Number of bytes in previous bins.
- size_t bin_slot_count_[kBinSize]; // Number of objects in a bin
+ // Mapping of oat filename to image data.
+ std::unordered_map<std::string, ImageInfo> image_info_map_;
// Cached size of the intern table for when we allocate memory.
size_t intern_table_bytes_;
@@ -434,6 +499,7 @@
// have one entry per art field for convenience. ArtFields are placed right after the end of the
// image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields.
struct NativeObjectRelocation {
+ const char* oat_filename;
uintptr_t offset;
NativeObjectRelocationType type;
@@ -468,6 +534,11 @@
// Which mode the image is stored as, see image.h
const ImageHeader::StorageMode image_storage_mode_;
+ // Map of dex files to the oat filenames that they were compiled into.
+ const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map_;
+ const std::vector<const char*> oat_filenames_;
+ const char* default_oat_filename_;
+
friend class ContainsBootClassLoaderNonImageClassVisitor;
friend class FixupClassVisitor;
friend class FixupRootVisitor;