diff options
Diffstat (limited to 'compiler')
22 files changed, 1836 insertions, 432 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 458973684e..6eeef3f567 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -143,7 +143,9 @@ LIBART_COMPILER_SRC_FILES_arm64 := \ jni/quick/arm64/calling_convention_arm64.cc \ linker/arm64/relative_patcher_arm64.cc \ optimizing/code_generator_arm64.cc \ + optimizing/instruction_simplifier_arm.cc \ optimizing/instruction_simplifier_arm64.cc \ + optimizing/instruction_simplifier_shared.cc \ optimizing/intrinsics_arm64.cc \ utils/arm64/assembler_arm64.cc \ utils/arm64/managed_register_arm64.cc \ diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index a7461a5525..46484b1cd6 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -100,12 +100,6 @@ class ElfBuilder FINAL { header_.sh_entsize = entsize; } - ~Section() OVERRIDE { - if (started_) { - CHECK(finished_); - } - } - // Start writing of this section. void Start() { CHECK(!started_); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 6859605095..12132c0cd0 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -95,25 +95,37 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { 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(), - /*compiling_boot_image*/true, - &timings, - &key_value_store); + const std::vector<const DexFile*>& dex_files = class_linker->GetBootClassPath(); std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), &compiler_driver_->GetCompilerOptions(), oat_file.GetFile()); - bool success = writer->PrepareImageAddressSpace(); - ASSERT_TRUE(success); - elf_writer->Start(); - + OatWriter oat_writer(/*compiling_boot_image*/true, &timings); OutputStream* rodata = elf_writer->StartRoData(); + for (const DexFile* dex_file : dex_files) { + ArrayRef<const uint8_t> raw_dex_file( + reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), + dex_file->GetHeader().file_size_); + oat_writer.AddRawDexFileSource(raw_dex_file, + dex_file->GetLocation().c_str(), + dex_file->GetLocationChecksum()); + } + std::unique_ptr<MemMap> opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + bool dex_files_ok = oat_writer.WriteAndOpenDexFiles( + rodata, + oat_file.GetFile(), + compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), + &key_value_store, + &opened_dex_files_map, + &opened_dex_files); + ASSERT_TRUE(dex_files_ok); + oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files); + bool image_space_ok = writer->PrepareImageAddressSpace(); + ASSERT_TRUE(image_space_ok); + bool rodata_ok = oat_writer.WriteRodata(rodata); ASSERT_TRUE(rodata_ok); elf_writer->EndRoData(rodata); @@ -123,12 +135,15 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ASSERT_TRUE(text_ok); elf_writer->EndText(text); + bool header_ok = oat_writer.WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); + ASSERT_TRUE(header_ok); + elf_writer->SetBssSize(oat_writer.GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations()); - success = elf_writer->End(); + bool success = elf_writer->End(); ASSERT_TRUE(success); } diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 7a2b74ed88..c0d15f3439 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -38,6 +38,7 @@ #include "oat_file-inl.h" #include "oat_writer.h" #include "scoped_thread_state_change.h" +#include "utils/test_dex_file_builder.h" namespace art { @@ -127,23 +128,74 @@ class OatTest : public CommonCompilerTest { const std::vector<const DexFile*>& dex_files, SafeMap<std::string, std::string>& key_value_store) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(dex_files, - 42U, - 4096U, - 0, - compiler_driver_.get(), - nullptr, - /*compiling_boot_image*/false, - &timings, - &key_value_store); + OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + for (const DexFile* dex_file : dex_files) { + ArrayRef<const uint8_t> raw_dex_file( + reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), + dex_file->GetHeader().file_size_); + if (!oat_writer.AddRawDexFileSource(raw_dex_file, + dex_file->GetLocation().c_str(), + dex_file->GetLocationChecksum())) { + return false; + } + } + return DoWriteElf(file, oat_writer, key_value_store); + } + + bool WriteElf(File* file, + const std::vector<const char*>& dex_filenames, + SafeMap<std::string, std::string>& key_value_store) { + TimingLogger timings("WriteElf", false, false); + OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + for (const char* dex_filename : dex_filenames) { + if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) { + return false; + } + } + return DoWriteElf(file, oat_writer, key_value_store); + } + + bool WriteElf(File* file, + ScopedFd&& zip_fd, + const char* location, + SafeMap<std::string, std::string>& key_value_store) { + TimingLogger timings("WriteElf", false, false); + OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) { + return false; + } + return DoWriteElf(file, oat_writer, key_value_store); + } + + bool DoWriteElf(File* file, + OatWriter& oat_writer, + SafeMap<std::string, std::string>& key_value_store) { std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), &compiler_driver_->GetCompilerOptions(), file); - elf_writer->Start(); - OutputStream* rodata = elf_writer->StartRoData(); + std::unique_ptr<MemMap> opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + if (!oat_writer.WriteAndOpenDexFiles(rodata, + file, + compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), + &key_value_store, + &opened_dex_files_map, + &opened_dex_files)) { + return false; + } + Runtime* runtime = Runtime::Current(); + ClassLinker* const class_linker = runtime->GetClassLinker(); + std::vector<const DexFile*> dex_files; + for (const std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { + dex_files.push_back(dex_file.get()); + ScopedObjectAccess soa(Thread::Current()); + class_linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc()); + } + oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files); if (!oat_writer.WriteRodata(rodata)) { return false; } @@ -155,6 +207,10 @@ class OatTest : public CommonCompilerTest { } elf_writer->EndText(text); + if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) { + return false; + } + elf_writer->SetBssSize(oat_writer.GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); @@ -167,6 +223,117 @@ class OatTest : public CommonCompilerTest { std::unique_ptr<QuickCompilerCallbacks> callbacks_; }; +class ZipBuilder { + public: + explicit ZipBuilder(File* zip_file) : zip_file_(zip_file) { } + + bool AddFile(const char* location, const void* data, size_t size) { + off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR); + if (offset == static_cast<off_t>(-1)) { + return false; + } + + ZipFileHeader file_header; + file_header.crc32 = crc32(0u, reinterpret_cast<const Bytef*>(data), size); + file_header.compressed_size = size; + file_header.uncompressed_size = size; + file_header.filename_length = strlen(location); + + if (!zip_file_->WriteFully(&file_header, sizeof(file_header)) || + !zip_file_->WriteFully(location, file_header.filename_length) || + !zip_file_->WriteFully(data, size)) { + return false; + } + + CentralDirectoryFileHeader cdfh; + cdfh.crc32 = file_header.crc32; + cdfh.compressed_size = size; + cdfh.uncompressed_size = size; + cdfh.filename_length = file_header.filename_length; + cdfh.relative_offset_of_local_file_header = offset; + file_data_.push_back(FileData { cdfh, location }); + return true; + } + + bool Finish() { + off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR); + if (offset == static_cast<off_t>(-1)) { + return false; + } + + size_t central_directory_size = 0u; + for (const FileData& file_data : file_data_) { + if (!zip_file_->WriteFully(&file_data.cdfh, sizeof(file_data.cdfh)) || + !zip_file_->WriteFully(file_data.location, file_data.cdfh.filename_length)) { + return false; + } + central_directory_size += sizeof(file_data.cdfh) + file_data.cdfh.filename_length; + } + EndOfCentralDirectoryRecord eocd_record; + eocd_record.number_of_central_directory_records_on_this_disk = file_data_.size(); + eocd_record.total_number_of_central_directory_records = file_data_.size(); + eocd_record.size_of_central_directory = central_directory_size; + eocd_record.offset_of_start_of_central_directory = offset; + return + zip_file_->WriteFully(&eocd_record, sizeof(eocd_record)) && + zip_file_->Flush() == 0; + } + + private: + struct PACKED(1) ZipFileHeader { + uint32_t signature = 0x04034b50; + uint16_t version_needed_to_extract = 10; + uint16_t general_purpose_bit_flag = 0; + uint16_t compression_method = 0; // 0 = store only. + uint16_t file_last_modification_time = 0u; + uint16_t file_last_modification_date = 0u; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_length; + uint16_t extra_field_length = 0u; // No extra fields. + }; + + struct PACKED(1) CentralDirectoryFileHeader { + uint32_t signature = 0x02014b50; + uint16_t version_made_by = 10; + uint16_t version_needed_to_extract = 10; + uint16_t general_purpose_bit_flag = 0; + uint16_t compression_method = 0; // 0 = store only. + uint16_t file_last_modification_time = 0u; + uint16_t file_last_modification_date = 0u; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_length; + uint16_t extra_field_length = 0u; // No extra fields. + uint16_t file_comment_length = 0u; // No file comment. + uint16_t disk_number_where_file_starts = 0u; + uint16_t internal_file_attributes = 0u; + uint32_t external_file_attributes = 0u; + uint32_t relative_offset_of_local_file_header; + }; + + struct PACKED(1) EndOfCentralDirectoryRecord { + uint32_t signature = 0x06054b50; + uint16_t number_of_this_disk = 0u; + uint16_t disk_where_central_directory_starts = 0u; + uint16_t number_of_central_directory_records_on_this_disk; + uint16_t total_number_of_central_directory_records; + uint32_t size_of_central_directory; + uint32_t offset_of_start_of_central_directory; + uint16_t comment_length = 0u; // No file comment. + }; + + struct FileData { + CentralDirectoryFileHeader cdfh; + const char* location; + }; + + File* zip_file_; + std::vector<FileData> file_data_; +}; + TEST_F(OatTest, WriteRead) { TimingLogger timings("OatTest::WriteRead", false, false); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -327,4 +494,189 @@ TEST_F(OatTest, EmptyTextSection) { EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength())); } +TEST_F(OatTest, DexFileInput) { + TimingLogger timings("OatTest::DexFileInput", false, false); + + std::vector<const char*> input_filenames; + + ScratchFile dex_file1; + TestDexFileBuilder builder1; + builder1.AddField("Lsome.TestClass;", "int", "someField"); + builder1.AddMethod("Lsome.TestClass;", "()I", "foo"); + std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename()); + bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(), + dex_file1_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file1.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + input_filenames.push_back(dex_file1.GetFilename().c_str()); + + ScratchFile dex_file2; + TestDexFileBuilder builder2; + builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField"); + builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar"); + std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename()); + success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(), + dex_file2_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file2.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + input_filenames.push_back(dex_file2.GetFilename().c_str()); + + ScratchFile oat_file; + SafeMap<std::string, std::string> key_value_store; + key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); + success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store); + ASSERT_TRUE(success); + + std::string error_msg; + std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), + oat_file.GetFilename(), + nullptr, + nullptr, + false, + nullptr, + &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); + std::unique_ptr<const DexFile> opened_dex_file1 = + opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); + std::unique_ptr<const DexFile> opened_dex_file2 = + opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), + &opened_dex_file1->GetHeader(), + dex_file1_data->GetHeader().file_size_)); + ASSERT_EQ(dex_file1_data->GetLocation(), opened_dex_file1->GetLocation()); + + ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), + &opened_dex_file2->GetHeader(), + dex_file2_data->GetHeader().file_size_)); + ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); +} + +TEST_F(OatTest, ZipFileInput) { + TimingLogger timings("OatTest::DexFileInput", false, false); + + ScratchFile zip_file; + ZipBuilder zip_builder(zip_file.GetFile()); + + ScratchFile dex_file1; + TestDexFileBuilder builder1; + builder1.AddField("Lsome.TestClass;", "long", "someField"); + builder1.AddMethod("Lsome.TestClass;", "()D", "foo"); + std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename()); + bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(), + dex_file1_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file1.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + success = zip_builder.AddFile("classes.dex", + &dex_file1_data->GetHeader(), + dex_file1_data->GetHeader().file_size_); + ASSERT_TRUE(success); + + ScratchFile dex_file2; + TestDexFileBuilder builder2; + builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField"); + builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar"); + std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename()); + success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(), + dex_file2_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file2.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + success = zip_builder.AddFile("classes2.dex", + &dex_file2_data->GetHeader(), + dex_file2_data->GetHeader().file_size_); + ASSERT_TRUE(success); + + success = zip_builder.Finish(); + ASSERT_TRUE(success) << strerror(errno); + + SafeMap<std::string, std::string> key_value_store; + key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); + { + // Test using the AddDexFileSource() interface with the zip file. + std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + + ScratchFile oat_file; + success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store); + ASSERT_TRUE(success); + + std::string error_msg; + std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), + oat_file.GetFilename(), + nullptr, + nullptr, + false, + nullptr, + &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); + std::unique_ptr<const DexFile> opened_dex_file1 = + opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); + std::unique_ptr<const DexFile> opened_dex_file2 = + opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), + &opened_dex_file1->GetHeader(), + dex_file1_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()), + opened_dex_file1->GetLocation()); + + ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), + &opened_dex_file2->GetHeader(), + dex_file2_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()), + opened_dex_file2->GetLocation()); + } + + { + // Test using the AddZipDexFileSource() interface with the zip file handle. + ScopedFd zip_fd(dup(zip_file.GetFd())); + ASSERT_NE(-1, zip_fd.get()); + + ScratchFile oat_file; + success = WriteElf(oat_file.GetFile(), + std::move(zip_fd), + zip_file.GetFilename().c_str(), + key_value_store); + ASSERT_TRUE(success); + + std::string error_msg; + std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), + oat_file.GetFilename(), + nullptr, + nullptr, + false, + nullptr, + &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); + std::unique_ptr<const DexFile> opened_dex_file1 = + opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); + std::unique_ptr<const DexFile> opened_dex_file2 = + opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), + &opened_dex_file1->GetHeader(), + dex_file1_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()), + opened_dex_file1->GetLocation()); + + ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), + &opened_dex_file2->GetHeader(), + dex_file2_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()), + opened_dex_file2->GetLocation()); + } +} + } // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 025e35e178..c74c41f0c9 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -16,12 +16,14 @@ #include "oat_writer.h" +#include <unistd.h> #include <zlib.h> #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method-inl.h" #include "base/allocator.h" #include "base/bit_vector.h" +#include "base/file_magic.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -49,9 +51,77 @@ #include "type_lookup_table.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "verifier/method_verifier.h" +#include "zip_archive.h" namespace art { +namespace { // anonymous namespace + +typedef DexFile::Header __attribute__((aligned(1))) UnalignedDexFileHeader; + +const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) { + return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data); +} + +} // anonymous namespace + +// Defines the location of the raw dex file to write. +class OatWriter::DexFileSource { + public: + explicit DexFileSource(ZipEntry* zip_entry) + : type_(kZipEntry), source_(zip_entry) { + DCHECK(source_ != nullptr); + } + + explicit DexFileSource(File* raw_file) + : type_(kRawFile), source_(raw_file) { + DCHECK(source_ != nullptr); + } + + explicit DexFileSource(const uint8_t* dex_file) + : type_(kRawData), source_(dex_file) { + DCHECK(source_ != nullptr); + } + + bool IsZipEntry() const { return type_ == kZipEntry; } + bool IsRawFile() const { return type_ == kRawFile; } + bool IsRawData() const { return type_ == kRawData; } + + ZipEntry* GetZipEntry() const { + DCHECK(IsZipEntry()); + DCHECK(source_ != nullptr); + return static_cast<ZipEntry*>(const_cast<void*>(source_)); + } + + File* GetRawFile() const { + DCHECK(IsRawFile()); + DCHECK(source_ != nullptr); + return static_cast<File*>(const_cast<void*>(source_)); + } + + const uint8_t* GetRawData() const { + DCHECK(IsRawData()); + DCHECK(source_ != nullptr); + return static_cast<const uint8_t*>(source_); + } + + void Clear() { + type_ = kNone; + source_ = nullptr; + } + + private: + enum Type { + kNone, + kZipEntry, + kRawFile, + kRawData, + }; + + Type type_; + const void* source_; +}; + class OatWriter::OatClass { public: OatClass(size_t offset, @@ -116,11 +186,30 @@ class OatWriter::OatClass { class OatWriter::OatDexFile { public: - OatDexFile(size_t offset, const DexFile& dex_file); + OatDexFile(const char* dex_file_location, + DexFileSource source, + CreateTypeLookupTable create_type_lookup_table); OatDexFile(OatDexFile&& src) = default; + const char* GetLocation() const { + return dex_file_location_data_; + } + + void ReserveTypeLookupTable(OatWriter* oat_writer); + void ReserveClassOffsets(OatWriter* oat_writer); + size_t SizeOf() const; - bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; + bool Write(OatWriter* oat_writer, OutputStream* out) const; + bool WriteClassOffsets(OatWriter* oat_writer, OutputStream* out); + + // The source of the dex file. + DexFileSource source_; + + // Whether to create the type lookup table. + CreateTypeLookupTable create_type_lookup_table_; + + // Dex file size. Initialized when writing the dex file. + size_t dex_file_size_; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. @@ -128,11 +217,13 @@ class OatWriter::OatDexFile { // Data to write. uint32_t dex_file_location_size_; - const uint8_t* dex_file_location_data_; + const char* dex_file_location_data_; uint32_t dex_file_location_checksum_; uint32_t dex_file_offset_; + uint32_t class_offsets_offset_; uint32_t lookup_table_offset_; - TypeLookupTable* lookup_table_; // Owned by the dex file. + + // Data to write to a separate section. dchecked_vector<uint32_t> class_offsets_; private: @@ -151,26 +242,20 @@ class OatWriter::OatDexFile { DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ << "file_offset=" << file_offset << " offset_=" << offset_ -OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, - uint32_t image_file_location_oat_checksum, - uintptr_t image_file_location_oat_begin, - 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), +OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) + : write_state_(WriteState::kAddingDexFileSources), + timings_(timings), + raw_dex_files_(), + zip_archives_(), + zipped_dex_files_(), + zipped_dex_file_locations_(), + compiler_driver_(nullptr), + image_writer_(nullptr), compiling_boot_image_(compiling_boot_image), - dex_files_(&dex_files), + dex_files_(nullptr), size_(0u), bss_size_(0u), oat_data_offset_(0u), - image_file_location_oat_checksum_(image_file_location_oat_checksum), - image_file_location_oat_begin_(image_file_location_oat_begin), - image_patch_delta_(image_patch_delta), - key_value_store_(key_value_store), oat_header_(nullptr), size_dex_file_alignment_(0), size_executable_offset_alignment_(0), @@ -197,55 +282,192 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_oat_dex_file_location_data_(0), size_oat_dex_file_location_checksum_(0), size_oat_dex_file_offset_(0), + size_oat_dex_file_class_offsets_offset_(0), size_oat_dex_file_lookup_table_offset_(0), - size_oat_dex_file_class_offsets_(0), size_oat_lookup_table_alignment_(0), size_oat_lookup_table_(0), + size_oat_class_offsets_alignment_(0), + size_oat_class_offsets_(0), size_oat_class_type_(0), size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), method_offset_map_() { - CHECK(key_value_store != nullptr); - if (compiling_boot_image) { - CHECK(image_writer != nullptr); +} + +bool OatWriter::AddDexFileSource(const char* filename, + const char* location, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + uint32_t magic; + std::string error_msg; + ScopedFd fd(OpenAndReadMagic(filename, &magic, &error_msg)); + if (fd.get() == -1) { + PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'"; + return false; + } else if (IsDexMagic(magic)) { + // The file is open for reading, not writing, so it's OK to let the File destructor + // close it without checking for explicit Close(), so pass checkUsage = false. + raw_dex_files_.emplace_back(new File(fd.release(), location, /* checkUsage */ false)); + oat_dex_files_.emplace_back(location, + DexFileSource(raw_dex_files_.back().get()), + create_type_lookup_table); + } else if (IsZipMagic(magic)) { + if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) { + return false; + } + } else { + LOG(ERROR) << "Expected valid zip or dex file: '" << filename << "'"; + return false; } - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); - const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); - relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features, - &method_offset_map_); + return true; +} - size_t offset; - { - TimingLogger::ScopedTiming split("InitOatHeader", timings); - offset = InitOatHeader(); +// Add dex file source(s) from a zip file specified by a file handle. +bool OatWriter::AddZippedDexFilesSource(ScopedFd&& zip_fd, + const char* location, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + std::string error_msg; + zip_archives_.emplace_back(ZipArchive::OpenFromFd(zip_fd.release(), location, &error_msg)); + ZipArchive* zip_archive = zip_archives_.back().get(); + if (zip_archive == nullptr) { + LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': " + << error_msg; + return false; } - { - TimingLogger::ScopedTiming split("InitOatDexFiles", timings); - offset = InitOatDexFiles(offset); + for (size_t i = 0; ; ++i) { + std::string entry_name = DexFile::GetMultiDexClassesDexName(i); + std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg)); + if (entry == nullptr) { + break; + } + zipped_dex_files_.push_back(std::move(entry)); + zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location)); + const char* full_location = zipped_dex_file_locations_.back().c_str(); + oat_dex_files_.emplace_back(full_location, + DexFileSource(zipped_dex_files_.back().get()), + create_type_lookup_table); + } + if (zipped_dex_file_locations_.empty()) { + LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; + return false; } - { - TimingLogger::ScopedTiming split("InitDexFiles", timings); - offset = InitDexFiles(offset); + return true; +} + +// Add dex file source from raw memory. +bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data, + const char* location, + uint32_t location_checksum, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + if (data.size() < sizeof(DexFile::Header)) { + LOG(ERROR) << "Provided data is shorter than dex file header. size: " + << data.size() << " File: " << location; + return false; } - { - TimingLogger::ScopedTiming split("InitLookupTables", timings); - offset = InitLookupTables(offset); + if (!ValidateDexFileHeader(data.data(), location)) { + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(data.data()); + if (data.size() < header->file_size_) { + LOG(ERROR) << "Truncated dex file data. Data size: " << data.size() + << " file size from header: " << header->file_size_ << " File: " << location; + return false; + } + + oat_dex_files_.emplace_back(location, DexFileSource(data.data()), create_type_lookup_table); + oat_dex_files_.back().dex_file_location_checksum_ = location_checksum; + return true; +} + +dchecked_vector<const char*> OatWriter::GetSourceLocations() const { + dchecked_vector<const char*> locations; + locations.reserve(oat_dex_files_.size()); + for (const OatDexFile& oat_dex_file : oat_dex_files_) { + locations.push_back(oat_dex_file.GetLocation()); } + return locations; +} + +bool OatWriter::WriteAndOpenDexFiles( + OutputStream* rodata, + File* file, + InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + SafeMap<std::string, std::string>* key_value_store, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { + CHECK(write_state_ == WriteState::kAddingDexFileSources); + + size_t offset = InitOatHeader(instruction_set, + instruction_set_features, + dchecked_integral_cast<uint32_t>(oat_dex_files_.size()), + key_value_store); + offset = InitOatDexFiles(offset); + size_ = offset; + + std::unique_ptr<MemMap> dex_files_map; + std::vector<std::unique_ptr<const DexFile>> dex_files; + if (!WriteDexFiles(rodata, file)) { + return false; + } + // Reserve space for type lookup tables and update type_lookup_table_offset_. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + oat_dex_file.ReserveTypeLookupTable(this); + } + size_t size_after_type_lookup_tables = size_; + // Reserve space for class offsets and update class_offsets_offset_. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + oat_dex_file.ReserveClassOffsets(this); + } + if (!WriteOatDexFiles(rodata) || + !ExtendForTypeLookupTables(rodata, file, size_after_type_lookup_tables) || + !OpenDexFiles(file, &dex_files_map, &dex_files) || + !WriteTypeLookupTables(dex_files_map.get(), dex_files)) { + return false; + } + + *opened_dex_files_map = std::move(dex_files_map); + *opened_dex_files = std::move(dex_files); + write_state_ = WriteState::kPrepareLayout; + return true; +} + +void OatWriter::PrepareLayout(const CompilerDriver* compiler, + ImageWriter* image_writer, + const std::vector<const DexFile*>& dex_files) { + CHECK(write_state_ == WriteState::kPrepareLayout); + + dex_files_ = &dex_files; + + compiler_driver_ = compiler; + image_writer_ = image_writer; + if (compiling_boot_image_) { + CHECK(image_writer_ != nullptr); + } + InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); + CHECK_EQ(instruction_set, oat_header_->GetInstructionSet()); + const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); + relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features, + &method_offset_map_); + + uint32_t offset = size_; { - TimingLogger::ScopedTiming split("InitOatClasses", timings); + TimingLogger::ScopedTiming split("InitOatClasses", timings_); offset = InitOatClasses(offset); } { - TimingLogger::ScopedTiming split("InitOatMaps", timings); + TimingLogger::ScopedTiming split("InitOatMaps", timings_); offset = InitOatMaps(offset); } { - TimingLogger::ScopedTiming split("InitOatCode", timings); + TimingLogger::ScopedTiming split("InitOatCode", timings_); offset = InitOatCode(offset); } { - TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings); + TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_); offset = InitOatCodeDexFiles(offset); } size_ = offset; @@ -255,7 +477,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_t bss_start = RoundUp(size_, kPageSize); size_t pointer_size = GetInstructionSetPointerSize(instruction_set); bss_size_ = 0u; - for (const DexFile* dex_file : dex_files) { + for (const DexFile* dex_file : *dex_files_) { dex_cache_arrays_offsets_.Put(dex_file, bss_start + bss_size_); DexCacheArraysLayout layout(pointer_size, dex_file); bss_size_ += layout.Size(); @@ -265,9 +487,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); if (compiling_boot_image_) { CHECK_EQ(image_writer_ != nullptr, - key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end()); + oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr); } - CHECK_ALIGNED(image_patch_delta_, kPageSize); + + write_state_ = WriteState::kWriteRoData; } OatWriter::~OatWriter() { @@ -1134,59 +1357,26 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { return true; } -size_t OatWriter::InitOatHeader() { - oat_header_.reset(OatHeader::Create(compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - dchecked_integral_cast<uint32_t>(dex_files_->size()), - key_value_store_)); - oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum_); - oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin_); - +size_t OatWriter::InitOatHeader(InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + uint32_t num_dex_files, + SafeMap<std::string, std::string>* key_value_store) { + TimingLogger::ScopedTiming split("InitOatHeader", timings_); + oat_header_.reset(OatHeader::Create(instruction_set, + instruction_set_features, + num_dex_files, + key_value_store)); + size_oat_header_ += sizeof(OatHeader); + size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); return oat_header_->GetHeaderSize(); } size_t OatWriter::InitOatDexFiles(size_t offset) { - // create the OatDexFiles - for (size_t i = 0; i != dex_files_->size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - CHECK(dex_file != nullptr); - oat_dex_files_.emplace_back(offset, *dex_file); - offset += oat_dex_files_.back().SizeOf(); - } - return offset; -} - -size_t OatWriter::InitDexFiles(size_t offset) { - // calculate the offsets within OatDexFiles to the DexFiles - for (size_t i = 0; i != dex_files_->size(); ++i) { - // dex files are required to be 4 byte aligned - size_t original_offset = offset; - offset = RoundUp(offset, 4); - size_dex_file_alignment_ += offset - original_offset; - - // set offset in OatDexFile to DexFile - oat_dex_files_[i].dex_file_offset_ = offset; - - const DexFile* dex_file = (*dex_files_)[i]; - - // Initialize type lookup table - oat_dex_files_[i].lookup_table_ = dex_file->GetTypeLookupTable(); - - offset += dex_file->GetHeader().file_size_; - } - return offset; -} - -size_t OatWriter::InitLookupTables(size_t offset) { + TimingLogger::ScopedTiming split("InitOatDexFiles", timings_); + // Initialize offsets of dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (oat_dex_file.lookup_table_ != nullptr) { - uint32_t aligned_offset = RoundUp(offset, 4); - oat_dex_file.lookup_table_offset_ = aligned_offset; - size_oat_lookup_table_alignment_ += aligned_offset - offset; - offset = aligned_offset + oat_dex_file.lookup_table_->RawDataLength(); - } else { - oat_dex_file.lookup_table_offset_ = 0; - } + oat_dex_file.offset_ = offset; + offset += oat_dex_file.SizeOf(); } return offset; } @@ -1239,7 +1429,6 @@ size_t OatWriter::InitOatCode(size_t offset) { oat_header_->SetExecutableOffset(offset); size_executable_offset_alignment_ = offset - old_offset; if (compiler_driver_->IsBootImage()) { - CHECK_EQ(image_patch_delta_, 0); InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); #define DO_TRAMPOLINE(field, fn_name) \ @@ -1264,7 +1453,6 @@ size_t OatWriter::InitOatCode(size_t offset) { oat_header_->SetQuickImtConflictTrampolineOffset(0); oat_header_->SetQuickResolutionTrampolineOffset(0); oat_header_->SetQuickToInterpreterBridgeOffset(0); - oat_header_->SetImagePatchDelta(image_patch_delta_); } return offset; } @@ -1289,22 +1477,15 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } bool OatWriter::WriteRodata(OutputStream* out) { - if (!GetOatDataOffset(out)) { - return false; - } - const size_t file_offset = oat_data_offset_; + CHECK(write_state_ == WriteState::kWriteRoData); - // Reserve space for header. It will be written last - after updating the checksum. - size_t header_size = oat_header_->GetHeaderSize(); - if (out->Seek(header_size, kSeekCurrent) == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to reserve space for oat header in " << out->GetLocation(); + if (!WriteClassOffsets(out)) { + LOG(ERROR) << "Failed to write class offsets to " << out->GetLocation(); return false; } - size_oat_header_ += sizeof(OatHeader); - size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); - if (!WriteTables(out, file_offset)) { - LOG(ERROR) << "Failed to write oat tables to " << out->GetLocation(); + if (!WriteClasses(out)) { + LOG(ERROR) << "Failed to write classes to " << out->GetLocation(); return false; } @@ -1313,6 +1494,7 @@ bool OatWriter::WriteRodata(OutputStream* out) { LOG(ERROR) << "Failed to seek to oat code position in " << out->GetLocation(); return false; } + size_t file_offset = oat_data_offset_; size_t relative_offset = static_cast<size_t>(tables_end_offset) - file_offset; relative_offset = WriteMaps(out, file_offset, relative_offset); if (relative_offset == 0) { @@ -1332,11 +1514,13 @@ bool OatWriter::WriteRodata(OutputStream* out) { } DCHECK_OFFSET(); + write_state_ = WriteState::kWriteText; return true; } bool OatWriter::WriteCode(OutputStream* out) { - size_t header_size = oat_header_->GetHeaderSize(); + CHECK(write_state_ == WriteState::kWriteText); + const size_t file_offset = oat_data_offset_; size_t relative_offset = oat_header_->GetExecutableOffset(); DCHECK_OFFSET(); @@ -1390,10 +1574,12 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_dex_file_location_data_); DO_STAT(size_oat_dex_file_location_checksum_); DO_STAT(size_oat_dex_file_offset_); + DO_STAT(size_oat_dex_file_class_offsets_offset_); DO_STAT(size_oat_dex_file_lookup_table_offset_); - DO_STAT(size_oat_dex_file_class_offsets_); DO_STAT(size_oat_lookup_table_alignment_); DO_STAT(size_oat_lookup_table_); + DO_STAT(size_oat_class_offsets_alignment_); + DO_STAT(size_oat_class_offsets_); DO_STAT(size_oat_class_type_); DO_STAT(size_oat_class_status_); DO_STAT(size_oat_class_method_bitmaps_); @@ -1408,89 +1594,91 @@ bool OatWriter::WriteCode(OutputStream* out) { CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset)); CHECK_EQ(size_, relative_offset); - // Finalize the header checksum. + write_state_ = WriteState::kWriteHeader; + return true; +} + +bool OatWriter::WriteHeader(OutputStream* out, + uint32_t image_file_location_oat_checksum, + uintptr_t image_file_location_oat_begin, + int32_t image_patch_delta) { + CHECK(write_state_ == WriteState::kWriteHeader); + + oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum); + oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin); + if (compiler_driver_->IsBootImage()) { + CHECK_EQ(image_patch_delta, 0); + CHECK_EQ(oat_header_->GetImagePatchDelta(), 0); + } else { + CHECK_ALIGNED(image_patch_delta, kPageSize); + oat_header_->SetImagePatchDelta(image_patch_delta); + } oat_header_->UpdateChecksumWithHeaderData(); - // Write the header now that the checksum is final. + const size_t file_offset = oat_data_offset_; + + off_t current_offset = out->Seek(0, kSeekCurrent); + if (current_offset == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Failed to get current offset from " << out->GetLocation(); + return false; + } if (out->Seek(file_offset, kSeekSet) == static_cast<off_t>(-1)) { PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation(); return false; } DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent))); + + // Flush all other data before writing the header. + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush before writing oat header to " << out->GetLocation(); + return false; + } + // Write the header. + size_t header_size = oat_header_->GetHeaderSize(); if (!out->WriteFully(oat_header_.get(), header_size)) { PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation(); return false; } - if (out->Seek(oat_end_file_offset, kSeekSet) == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to seek to end after writing oat header to " << out->GetLocation(); + // Flush the header data. + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush after writing oat header to " << out->GetLocation(); + return false; + } + + if (out->Seek(current_offset, kSeekSet) == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Failed to seek back after writing oat header to " << out->GetLocation(); return false; } - DCHECK_EQ(oat_end_file_offset, out->Seek(0, kSeekCurrent)); + DCHECK_EQ(current_offset, out->Seek(0, kSeekCurrent)); + write_state_ = WriteState::kDone; return true; } -bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { - for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - if (!oat_dex_files_[i].Write(this, out, file_offset)) { - PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation(); - return false; - } - } - for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - uint32_t expected_offset = file_offset + oat_dex_files_[i].dex_file_offset_; - off_t actual_offset = out->Seek(expected_offset, kSeekSet); - if (static_cast<uint32_t>(actual_offset) != expected_offset) { - const DexFile* dex_file = (*dex_files_)[i]; - PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset - << " Expected: " << expected_offset << " File: " << dex_file->GetLocation(); - return false; - } - const DexFile* dex_file = (*dex_files_)[i]; - if (!out->WriteFully(&dex_file->GetHeader(), dex_file->GetHeader().file_size_)) { - PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() - << " to " << out->GetLocation(); - return false; - } - size_dex_file_ += dex_file->GetHeader().file_size_; - } - if (!WriteLookupTables(out, file_offset)) { - return false; - } - for (size_t i = 0; i != oat_classes_.size(); ++i) { - if (!oat_classes_[i].Write(this, out, file_offset)) { - PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation(); - return false; +bool OatWriter::WriteClassOffsets(OutputStream* out) { + for (OatDexFile& oat_dex_file : oat_dex_files_) { + if (oat_dex_file.class_offsets_offset_ != 0u) { + uint32_t expected_offset = oat_data_offset_ + oat_dex_file.class_offsets_offset_; + off_t actual_offset = out->Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + PLOG(ERROR) << "Failed to seek to oat class offsets section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << oat_dex_file.GetLocation(); + return false; + } + if (!oat_dex_file.WriteClassOffsets(this, out)) { + return false; + } } } return true; } -bool OatWriter::WriteLookupTables(OutputStream* out, const size_t file_offset) { - for (size_t i = 0; i < oat_dex_files_.size(); ++i) { - const uint32_t lookup_table_offset = oat_dex_files_[i].lookup_table_offset_; - const TypeLookupTable* table = oat_dex_files_[i].lookup_table_; - DCHECK_EQ(lookup_table_offset == 0, table == nullptr); - if (lookup_table_offset == 0) { - continue; - } - const uint32_t expected_offset = file_offset + lookup_table_offset; - off_t actual_offset = out->Seek(expected_offset, kSeekSet); - if (static_cast<uint32_t>(actual_offset) != expected_offset) { - const DexFile* dex_file = (*dex_files_)[i]; - PLOG(ERROR) << "Failed to seek to lookup table section. Actual: " << actual_offset - << " Expected: " << expected_offset << " File: " << dex_file->GetLocation(); +bool OatWriter::WriteClasses(OutputStream* out) { + for (OatClass& oat_class : oat_classes_) { + if (!oat_class.Write(this, out, oat_data_offset_)) { + PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation(); return false; } - if (table != nullptr) { - if (!WriteData(out, table->RawData(), table->RawDataLength())) { - const DexFile* dex_file = (*dex_files_)[i]; - PLOG(ERROR) << "Failed to write lookup table for " << dex_file->GetLocation() - << " to " << out->GetLocation(); - return false; - } - size_oat_lookup_table_ += table->RawDataLength(); - } } return true; } @@ -1585,6 +1773,455 @@ bool OatWriter::GetOatDataOffset(OutputStream* out) { return true; } +bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { + // Read the dex file header and perform minimal verification. + uint8_t raw_header[sizeof(DexFile::Header)]; + if (!file->ReadFully(&raw_header, sizeof(DexFile::Header))) { + PLOG(ERROR) << "Failed to read dex file header. Actual: " + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!ValidateDexFileHeader(raw_header, oat_dex_file->GetLocation())) { + return false; + } + + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); + oat_dex_file->dex_file_size_ = header->file_size_; + oat_dex_file->dex_file_location_checksum_ = header->checksum_; + oat_dex_file->class_offsets_.resize(header->class_defs_size_); + return true; +} + +bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { + if (!DexFile::IsMagicValid(raw_header)) { + LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; + return false; + } + if (!DexFile::IsVersionValid(raw_header)) { + LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); + if (header->file_size_ < sizeof(DexFile::Header)) { + LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." + << " File: " << location; + return false; + } + return true; +} + +bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) { + TimingLogger::ScopedTiming split("WriteDexFiles", timings_); + + // Get the elf file offset of the oat file. + if (!GetOatDataOffset(rodata)) { + return false; + } + + // Write dex files. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + if (!WriteDexFile(rodata, file, &oat_dex_file)) { + return false; + } + } + + // Close sources. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated. + } + zipped_dex_files_.clear(); + zip_archives_.clear(); + raw_dex_files_.clear(); + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file) { + if (!SeekToDexFile(rodata, file, oat_dex_file)) { + return false; + } + if (oat_dex_file->source_.IsZipEntry()) { + if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { + return false; + } + } else if (oat_dex_file->source_.IsRawFile()) { + if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { + return false; + } + } else { + DCHECK(oat_dex_file->source_.IsRawData()); + if (!WriteDexFile(rodata, oat_dex_file, oat_dex_file->source_.GetRawData())) { + return false; + } + } + + // Update current size and account for the written data. + DCHECK_EQ(size_, oat_dex_file->dex_file_offset_); + size_ += oat_dex_file->dex_file_size_; + size_dex_file_ += oat_dex_file->dex_file_size_; + return true; +} + +bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { + // Dex files are required to be 4 byte aligned. + size_t original_offset = size_; + size_t offset = RoundUp(original_offset, 4); + size_dex_file_alignment_ += offset - original_offset; + + // Seek to the start of the dex file and flush any pending operations in the stream. + // Verify that, after flushing the stream, the file is at the same offset as the stream. + uint32_t start_offset = oat_data_offset_ + offset; + off_t actual_offset = out->Seek(start_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(start_offset)) { + PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset + << " Expected: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush before writing dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + actual_offset = lseek(file->Fd(), 0, SEEK_CUR); + if (actual_offset != static_cast<off_t>(start_offset)) { + PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset + << " Expected: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + size_ = offset; + oat_dex_file->dex_file_offset_ = offset; + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, + File* file, + OatDexFile* oat_dex_file, + ZipEntry* dex_file) { + size_t start_offset = oat_data_offset_ + size_; + DCHECK_EQ(static_cast<off_t>(start_offset), rodata->Seek(0, kSeekCurrent)); + + // Extract the dex file and get the extracted size. + std::string error_msg; + if (!dex_file->ExtractToFile(*file, &error_msg)) { + LOG(ERROR) << "Failed to extract dex file from ZIP entry: " << error_msg + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (file->Flush() != 0) { + PLOG(ERROR) << "Failed to flush dex file from ZIP entry." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + off_t extracted_end = lseek(file->Fd(), 0, SEEK_CUR); + if (extracted_end == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Failed get end offset after writing dex file from ZIP entry." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (extracted_end < static_cast<off_t>(start_offset)) { + LOG(ERROR) << "Dex file end position is before start position! End: " << extracted_end + << " Start: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + uint64_t extracted_size = static_cast<uint64_t>(extracted_end - start_offset); + if (extracted_size < sizeof(DexFile::Header)) { + LOG(ERROR) << "Extracted dex file is shorter than dex file header. size: " + << extracted_size << " File: " << oat_dex_file->GetLocation(); + return false; + } + + // Read the dex file header and extract required data to OatDexFile. + off_t actual_offset = lseek(file->Fd(), start_offset, SEEK_SET); + if (actual_offset != static_cast<off_t>(start_offset)) { + PLOG(ERROR) << "Failed to seek back to dex file header. Actual: " << actual_offset + << " Expected: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!ReadDexFileHeader(file, oat_dex_file)) { + return false; + } + if (extracted_size < oat_dex_file->dex_file_size_) { + LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size + << " file size from header: " << oat_dex_file->dex_file_size_ + << " File: " << oat_dex_file->GetLocation(); + return false; + } + + // Override the checksum from header with the CRC from ZIP entry. + oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); + + // Seek both file and stream to the end offset. + size_t end_offset = start_offset + oat_dex_file->dex_file_size_; + actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Failed to seek to end of dex file. Actual: " << actual_offset + << " Expected: " << end_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + actual_offset = rodata->Seek(end_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset + << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after seeking over dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + // If we extracted more than the size specified in the header, truncate the file. + if (extracted_size > oat_dex_file->dex_file_size_) { + if (file->SetLength(end_offset) != 0) { + PLOG(ERROR) << "Failed to truncate excessive dex file length." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + } + + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, + File* file, + OatDexFile* oat_dex_file, + File* dex_file) { + size_t start_offset = oat_data_offset_ + size_; + DCHECK_EQ(static_cast<off_t>(start_offset), rodata->Seek(0, kSeekCurrent)); + + off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET); + if (input_offset != static_cast<off_t>(0)) { + PLOG(ERROR) << "Failed to seek to dex file header. Actual: " << input_offset + << " Expected: 0" + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!ReadDexFileHeader(dex_file, oat_dex_file)) { + return false; + } + + // Copy the input dex file using sendfile(). + if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) { + PLOG(ERROR) << "Failed to copy dex file to oat file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (file->Flush() != 0) { + PLOG(ERROR) << "Failed to flush dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + // Check file position and seek the stream to the end offset. + size_t end_offset = start_offset + oat_dex_file->dex_file_size_; + off_t actual_offset = lseek(file->Fd(), 0, SEEK_CUR); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Unexpected file position after copying dex file. Actual: " << actual_offset + << " Expected: " << end_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + actual_offset = rodata->Seek(end_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset + << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after seeking over dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, + OatDexFile* oat_dex_file, + const uint8_t* dex_file) { + // Note: The raw data has already been checked to contain the header + // and all the data that the header specifies as the file size. + DCHECK(dex_file != nullptr); + DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation())); + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file); + + if (!rodata->WriteFully(dex_file, header->file_size_)) { + PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation() + << " to " << rodata->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing dex file." + << " File: " << oat_dex_file->GetLocation(); + return false; + } + + // Update dex file size and resize class offsets in the OatDexFile. + // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). + oat_dex_file->dex_file_size_ = header->file_size_; + oat_dex_file->class_offsets_.resize(header->class_defs_size_); + return true; +} + +bool OatWriter::WriteOatDexFiles(OutputStream* rodata) { + TimingLogger::ScopedTiming split("WriteOatDexFiles", timings_); + + // Seek to the start of OatDexFiles, i.e. to the end of the OatHeader. If there are + // no OatDexFiles, no data is actually written to .rodata before WriteHeader() and + // this Seek() ensures that we reserve the space for OatHeader in .rodata. + DCHECK(oat_dex_files_.empty() || oat_dex_files_[0u].offset_ == oat_header_->GetHeaderSize()); + uint32_t expected_offset = oat_data_offset_ + oat_header_->GetHeaderSize(); + off_t actual_offset = rodata->Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + PLOG(ERROR) << "Failed to seek to OatDexFile table section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << rodata->GetLocation(); + return false; + } + + for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) { + OatDexFile* oat_dex_file = &oat_dex_files_[i]; + + DCHECK_EQ(oat_data_offset_ + oat_dex_file->offset_, + static_cast<size_t>(rodata->Seek(0, kSeekCurrent))); + + // Write OatDexFile. + if (!oat_dex_file->Write(this, rodata)) { + PLOG(ERROR) << "Failed to write oat dex information to " << rodata->GetLocation(); + return false; + } + } + + return true; +} + +bool OatWriter::ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset) { + TimingLogger::ScopedTiming split("ExtendForTypeLookupTables", timings_); + + int64_t new_length = oat_data_offset_ + dchecked_integral_cast<int64_t>(offset); + if (file->SetLength(new_length) != 0) { + PLOG(ERROR) << "Failed to extend file for type lookup tables. new_length: " << new_length + << "File: " << file->GetPath(); + return false; + } + off_t actual_offset = rodata->Seek(new_length, kSeekSet); + if (actual_offset != static_cast<off_t>(new_length)) { + PLOG(ERROR) << "Failed to seek stream after extending file for type lookup tables." + << " Actual: " << actual_offset << " Expected: " << new_length + << " File: " << rodata->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after extending for type lookup tables." + << " File: " << rodata->GetLocation(); + return false; + } + return true; +} + +bool OatWriter::OpenDexFiles( + File* file, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { + TimingLogger::ScopedTiming split("OpenDexFiles", timings_); + + if (oat_dex_files_.empty()) { + // Nothing to do. + return true; + } + + size_t map_offset = oat_dex_files_[0].dex_file_offset_; + size_t length = size_ - map_offset; + std::string error_msg; + std::unique_ptr<MemMap> dex_files_map(MemMap::MapFile(length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + file->Fd(), + oat_data_offset_ + map_offset, + /* low_4gb */ false, + file->GetPath().c_str(), + &error_msg)); + if (dex_files_map == nullptr) { + LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath() + << " error: " << error_msg; + return false; + } + std::vector<std::unique_ptr<const DexFile>> dex_files; + for (OatDexFile& oat_dex_file : oat_dex_files_) { + // Make sure no one messed with input files while we were copying data. + // At the very least we need consistent file size and number of class definitions. + const uint8_t* raw_dex_file = + dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; + if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { + // Note: ValidateDexFileHeader() already logged an error message. + LOG(ERROR) << "Failed to verify written dex file header!" + << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset + << " ~ " << static_cast<const void*>(raw_dex_file); + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); + if (header->file_size_ != oat_dex_file.dex_file_size_) { + LOG(ERROR) << "File size mismatch in written dex file header! Expected: " + << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ + << " Output: " << file->GetPath(); + return false; + } + if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { + LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " + << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ + << " Output: " << file->GetPath(); + return false; + } + + // Now, open the dex file. + dex_files.emplace_back(DexFile::Open(raw_dex_file, + oat_dex_file.dex_file_size_, + oat_dex_file.GetLocation(), + oat_dex_file.dex_file_location_checksum_, + /* oat_dex_file */ nullptr, + &error_msg)); + if (dex_files.back() == nullptr) { + LOG(ERROR) << "Failed to open dex file from oat file. File:" << oat_dex_file.GetLocation(); + return false; + } + } + + *opened_dex_files_map = std::move(dex_files_map); + *opened_dex_files = std::move(dex_files); + return true; +} + +bool OatWriter::WriteTypeLookupTables( + MemMap* opened_dex_files_map, + const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) { + TimingLogger::ScopedTiming split("WriteTypeLookupTables", timings_); + + DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size()); + for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) { + OatDexFile* oat_dex_file = &oat_dex_files_[i]; + if (oat_dex_file->lookup_table_offset_ != 0u) { + DCHECK(oat_dex_file->create_type_lookup_table_ == CreateTypeLookupTable::kCreate); + DCHECK_NE(oat_dex_file->class_offsets_.size(), 0u); + size_t map_offset = oat_dex_files_[0].dex_file_offset_; + size_t lookup_table_offset = oat_dex_file->lookup_table_offset_; + uint8_t* lookup_table = opened_dex_files_map->Begin() + (lookup_table_offset - map_offset); + opened_dex_files[i]->CreateTypeLookupTable(lookup_table); + } + } + + DCHECK_EQ(opened_dex_files_map == nullptr, opened_dex_files.empty()); + if (opened_dex_files_map != nullptr && !opened_dex_files_map->Sync()) { + PLOG(ERROR) << "Failed to Sync() type lookup tables. Map: " << opened_dex_files_map->GetName(); + return false; + } + + return true; +} + bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) { static const uint8_t kPadding[] = { 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u @@ -1611,15 +2248,20 @@ std::pair<bool, uint32_t> OatWriter::MethodOffsetMap::FindMethodOffset(MethodRef } } -OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) { - offset_ = offset; - const std::string& location(dex_file.GetLocation()); - dex_file_location_size_ = location.size(); - dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data()); - dex_file_location_checksum_ = dex_file.GetLocationChecksum(); - dex_file_offset_ = 0; - lookup_table_offset_ = 0; - class_offsets_.resize(dex_file.NumClassDefs()); +OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, + DexFileSource source, + CreateTypeLookupTable create_type_lookup_table) + : source_(source), + create_type_lookup_table_(create_type_lookup_table), + dex_file_size_(0), + offset_(0), + dex_file_location_size_(strlen(dex_file_location)), + dex_file_location_data_(dex_file_location), + dex_file_location_checksum_(0u), + dex_file_offset_(0u), + class_offsets_offset_(0u), + lookup_table_offset_(0u), + class_offsets_() { } size_t OatWriter::OatDexFile::SizeOf() const { @@ -1627,24 +2269,54 @@ size_t OatWriter::OatDexFile::SizeOf() const { + dex_file_location_size_ + sizeof(dex_file_location_checksum_) + sizeof(dex_file_offset_) - + sizeof(lookup_table_offset_) - + (sizeof(class_offsets_[0]) * class_offsets_.size()); + + sizeof(class_offsets_offset_) + + sizeof(lookup_table_offset_); +} + +void OatWriter::OatDexFile::ReserveTypeLookupTable(OatWriter* oat_writer) { + DCHECK_EQ(lookup_table_offset_, 0u); + if (create_type_lookup_table_ == CreateTypeLookupTable::kCreate && !class_offsets_.empty()) { + size_t table_size = TypeLookupTable::RawDataLength(class_offsets_.size()); + if (table_size != 0u) { + // Type tables are required to be 4 byte aligned. + size_t original_offset = oat_writer->size_; + size_t offset = RoundUp(original_offset, 4); + oat_writer->size_oat_lookup_table_alignment_ += offset - original_offset; + lookup_table_offset_ = offset; + oat_writer->size_ = offset + table_size; + oat_writer->size_oat_lookup_table_ += table_size; + } + } } -bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, - OutputStream* out, - const size_t file_offset) const { +void OatWriter::OatDexFile::ReserveClassOffsets(OatWriter* oat_writer) { + DCHECK_EQ(class_offsets_offset_, 0u); + if (!class_offsets_.empty()) { + // Class offsets are required to be 4 byte aligned. + size_t original_offset = oat_writer->size_; + size_t offset = RoundUp(original_offset, 4); + oat_writer->size_oat_class_offsets_alignment_ += offset - original_offset; + class_offsets_offset_ = offset; + oat_writer->size_ = offset + GetClassOffsetsRawSize(); + } +} + +bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) const { + const size_t file_offset = oat_writer->oat_data_offset_; DCHECK_OFFSET_(); + if (!oat_writer->WriteData(out, &dex_file_location_size_, sizeof(dex_file_location_size_))) { PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_); + if (!oat_writer->WriteData(out, dex_file_location_data_, dex_file_location_size_)) { PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_; + if (!oat_writer->WriteData(out, &dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { @@ -1652,21 +2324,35 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, return false; } oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_); + if (!oat_writer->WriteData(out, &dex_file_offset_, sizeof(dex_file_offset_))) { PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); + + if (!oat_writer->WriteData(out, &class_offsets_offset_, sizeof(class_offsets_offset_))) { + PLOG(ERROR) << "Failed to write class offsets offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_class_offsets_offset_ += sizeof(class_offsets_offset_); + if (!oat_writer->WriteData(out, &lookup_table_offset_, sizeof(lookup_table_offset_))) { PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_); + + return true; +} + +bool OatWriter::OatDexFile::WriteClassOffsets(OatWriter* oat_writer, OutputStream* out) { if (!oat_writer->WriteData(out, class_offsets_.data(), GetClassOffsetsRawSize())) { - PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation(); + PLOG(ERROR) << "Failed to write oat class offsets for " << GetLocation() + << " to " << out->GetLocation(); return false; } - oat_writer->size_oat_dex_file_class_offsets_ += GetClassOffsetsRawSize(); + oat_writer->size_oat_class_offsets_ += GetClassOffsetsRawSize(); return true; } diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 5feb5fc516..d681998774 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -27,7 +27,9 @@ #include "method_reference.h" #include "mirror/class.h" #include "oat.h" +#include "os.h" #include "safe_map.h" +#include "ScopedFd.h" #include "utils/array_ref.h" namespace art { @@ -39,6 +41,7 @@ class ImageWriter; class OutputStream; class TimingLogger; class TypeLookupTable; +class ZipEntry; namespace dwarf { struct MethodDebugInfo; @@ -61,6 +64,11 @@ struct MethodDebugInfo; // ... // TypeLookupTable[D] // +// ClassOffsets[0] one table of OatClass offsets for each class def for each OatDexFile. +// ClassOffsets[1] +// ... +// ClassOffsets[D] +// // OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs // OatClass[1] contains OatClass entries with class status, offsets to code, etc. // ... @@ -93,15 +101,65 @@ struct MethodDebugInfo; // class OatWriter { public: - OatWriter(const std::vector<const DexFile*>& dex_files, - uint32_t image_file_location_oat_checksum, - uintptr_t image_file_location_oat_begin, - 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); + enum class CreateTypeLookupTable { + kCreate, + kDontCreate, + kDefault = kCreate + }; + + OatWriter(bool compiling_boot_image, TimingLogger* timings); + + // To produce a valid oat file, the user must first add sources with any combination of + // - AddDexFileSource(), + // - AddZippedDexFilesSource(), + // - AddRawDexFileSource(). + // Then the user must call in order + // - WriteAndOpenDexFiles() + // - PrepareLayout(), + // - WriteRodata(), + // - WriteCode(), + // - WriteHeader(). + + // Add dex file source(s) from a file, either a plain dex file or + // a zip file with one or more dex files. + bool AddDexFileSource( + const char* filename, + const char* location, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + // Add dex file source(s) from a zip file specified by a file handle. + bool AddZippedDexFilesSource( + ScopedFd&& zip_fd, + const char* location, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + // Add dex file source from raw memory. + bool AddRawDexFileSource( + const ArrayRef<const uint8_t>& data, + const char* location, + uint32_t location_checksum, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + dchecked_vector<const char*> GetSourceLocations() const; + + // Write raw dex files to the .rodata section and open them from the oat file. + bool WriteAndOpenDexFiles(OutputStream* rodata, + File* file, + InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + SafeMap<std::string, std::string>* key_value_store, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); + // Prepare layout of remaining data. + void PrepareLayout(const CompilerDriver* compiler, + ImageWriter* image_writer, + const std::vector<const DexFile*>& dex_files); + // Write the rest of .rodata section (ClassOffsets[], OatClass[], maps). + bool WriteRodata(OutputStream* out); + // Write the code to the .text section. + bool WriteCode(OutputStream* out); + // Write the oat header. This finalizes the oat file. + bool WriteHeader(OutputStream* out, + uint32_t image_file_location_oat_checksum, + uintptr_t image_file_location_oat_begin, + int32_t image_patch_delta); // Returns whether the oat file has an associated image. bool HasImage() const { @@ -130,9 +188,6 @@ class OatWriter { return ArrayRef<const uintptr_t>(absolute_patch_locations_); } - bool WriteRodata(OutputStream* out); - bool WriteCode(OutputStream* out); - ~OatWriter(); ArrayRef<const dwarf::MethodDebugInfo> GetMethodDebugInfo() const { @@ -144,6 +199,7 @@ class OatWriter { } private: + class DexFileSource; class OatClass; class OatDexFile; @@ -174,29 +230,65 @@ class OatWriter { // with a given DexMethodVisitor. bool VisitDexMethods(DexMethodVisitor* visitor); - size_t InitOatHeader(); + size_t InitOatHeader(InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + uint32_t num_dex_files, + SafeMap<std::string, std::string>* key_value_store); size_t InitOatDexFiles(size_t offset); - size_t InitLookupTables(size_t offset); - size_t InitDexFiles(size_t offset); size_t InitOatClasses(size_t offset); size_t InitOatMaps(size_t offset); size_t InitOatCode(size_t offset); size_t InitOatCodeDexFiles(size_t offset); - bool WriteTables(OutputStream* out, const size_t file_offset); - bool WriteLookupTables(OutputStream* out, const size_t file_offset); + bool WriteClassOffsets(OutputStream* out); + bool WriteClasses(OutputStream* out); size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); bool GetOatDataOffset(OutputStream* out); + bool ReadDexFileHeader(File* file, OatDexFile* oat_dex_file); + bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); + bool WriteDexFiles(OutputStream* rodata, File* file); + bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file); + bool SeekToDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file); + bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file); + bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, File* dex_file); + bool WriteDexFile(OutputStream* rodata, OatDexFile* oat_dex_file, const uint8_t* dex_file); + bool WriteOatDexFiles(OutputStream* rodata); + bool ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset); + bool OpenDexFiles(File* file, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); + bool WriteTypeLookupTables(MemMap* opened_dex_files_map, + const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); bool WriteData(OutputStream* out, const void* data, size_t size); + enum class WriteState { + kAddingDexFileSources, + kPrepareLayout, + kWriteRoData, + kWriteText, + kWriteHeader, + kDone + }; + + WriteState write_state_; + TimingLogger* timings_; + + std::vector<std::unique_ptr<File>> raw_dex_files_; + std::vector<std::unique_ptr<ZipArchive>> zip_archives_; + std::vector<std::unique_ptr<ZipEntry>> zipped_dex_files_; + + // Using std::list<> which doesn't move elements around on push/emplace_back(). + // We need this because we keep plain pointers to the strings' c_str(). + std::list<std::string> zipped_dex_file_locations_; + dchecked_vector<dwarf::MethodDebugInfo> method_info_; - const CompilerDriver* const compiler_driver_; - ImageWriter* const image_writer_; + const CompilerDriver* compiler_driver_; + ImageWriter* image_writer_; const bool compiling_boot_image_; // note OatFile does not take ownership of the DexFiles @@ -215,13 +307,7 @@ class OatWriter { // Offset of the oat data from the start of the mmapped region of the elf file. size_t oat_data_offset_; - // dependencies on the image. - uint32_t image_file_location_oat_checksum_; - uintptr_t image_file_location_oat_begin_; - int32_t image_patch_delta_; - // data to write - SafeMap<std::string, std::string>* key_value_store_; std::unique_ptr<OatHeader> oat_header_; dchecked_vector<OatDexFile> oat_dex_files_; dchecked_vector<OatClass> oat_classes_; @@ -257,10 +343,12 @@ class OatWriter { uint32_t size_oat_dex_file_location_data_; uint32_t size_oat_dex_file_location_checksum_; uint32_t size_oat_dex_file_offset_; + uint32_t size_oat_dex_file_class_offsets_offset_; uint32_t size_oat_dex_file_lookup_table_offset_; - uint32_t size_oat_dex_file_class_offsets_; uint32_t size_oat_lookup_table_alignment_; uint32_t size_oat_lookup_table_; + uint32_t size_oat_class_offsets_alignment_; + uint32_t size_oat_class_offsets_; uint32_t size_oat_class_type_; uint32_t size_oat_class_status_; uint32_t size_oat_class_method_bitmaps_; @@ -269,7 +357,7 @@ class OatWriter { std::unique_ptr<linker::RelativePatcher> relative_patcher_; // The locations of absolute patches relative to the start of the executable section. - std::vector<uintptr_t> absolute_patch_locations_; + dchecked_vector<uintptr_t> absolute_patch_locations_; // Map method reference to assigned offset. // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 272579219f..f265a0c7d3 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -6449,6 +6449,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m return DeduplicateMethodLiteral(target_method, &call_patches_); } +void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = instr->GetLocations(); + Register res = locations->Out().AsRegister<Register>(); + Register accumulator = locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex) + .AsRegister<Register>(); + Register mul_left = locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex) + .AsRegister<Register>(); + Register mul_right = locations->InAt(HMultiplyAccumulate::kInputMulRightIndex) + .AsRegister<Register>(); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ mla(res, mul_left, mul_right, accumulator); + } else { + __ mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index d45ea973f9..df2126c653 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 3d65e9c53c..3fdd7186d1 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1952,21 +1952,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( Operand(InputOperandAt(instruction, 1))); } -void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); - locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex, - Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (instr->GetOpKind() == HInstruction::kSub && + accumulator->IsConstant() && + accumulator->AsConstant()->IsZero()) { + // Don't allocate register for Mneg instruction. + } else { + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + } + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { Register res = OutputRegister(instr); - Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex); - Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex); - Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex); + Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); // Avoid emitting code that could trigger Cortex A53's erratum 835769. // This fixup should be carried out for all multiply-accumulate instructions: @@ -1986,10 +1992,18 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA } if (instr->GetOpKind() == HInstruction::kAdd) { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); __ Madd(res, mul_left, mul_right, accumulator); } else { DCHECK(instr->GetOpKind() == HInstruction::kSub); - __ Msub(res, mul_left, mul_right, accumulator); + HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) { + __ Mneg(res, mul_left, mul_right); + } else { + Register accumulator = InputRegisterAt(instr, + HMultiplyAccumulate::kInputAccumulatorIndex); + __ Msub(res, mul_left, mul_right, accumulator); + } } } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 8eb9fcc558..98303f67ad 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -195,6 +195,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -245,6 +246,7 @@ class LocationsBuilderARM64 : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 32c3a925e0..3c88ba7ba7 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -426,6 +426,12 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); } +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) + void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } +#endif + #ifdef ART_ENABLE_CODEGEN_arm64 void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); @@ -433,10 +439,6 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } - - void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE { - StartAttributeStream("kind") << instruction->GetOpKind(); - } #endif bool IsPass(const char* name) { diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc new file mode 100644 index 0000000000..db1f9a79aa --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_arm.h" +#include "instruction_simplifier_shared.h" + +namespace art { +namespace arm { + +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +} // namespace arm +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h new file mode 100644 index 0000000000..379b95d6ae --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { +namespace arm { + +class InstructionSimplifierArmVisitor : public HGraphVisitor { + public: + InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats) + : HGraphVisitor(graph), stats_(stats) {} + + private: + void RecordSimplification() { + if (stats_ != nullptr) { + stats_->RecordStat(kInstructionSimplificationsArch); + } + } + + void VisitMul(HMul* instruction) OVERRIDE; + + OptimizingCompilerStats* stats_; +}; + + +class InstructionSimplifierArm : public HOptimization { + public: + InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats) + : HOptimization(graph, "instruction_simplifier_arm", stats) {} + + void Run() OVERRIDE { + InstructionSimplifierArmVisitor visitor(graph_, stats_); + visitor.VisitReversePostOrder(); + } +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 4bcfc54791..83126a5c4d 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -17,6 +17,7 @@ #include "instruction_simplifier_arm64.h" #include "common_arm64.h" +#include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" namespace art { @@ -179,67 +180,6 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } -bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns( - HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) { - DCHECK(Primitive::IsIntOrLongType(mul->GetType())); - DCHECK(input_binop->IsAdd() || input_binop->IsSub()); - DCHECK_NE(input_binop, input_other); - if (!input_binop->HasOnlyOneNonEnvironmentUse()) { - return false; - } - - // Try to interpret patterns like - // a * (b <+/-> 1) - // as - // (a * b) <+/-> a - HInstruction* input_a = input_other; - HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. - HInstruction::InstructionKind op_kind; - - if (input_binop->IsAdd()) { - if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { - // Interpret - // a * (b + 1) - // as - // (a * b) + a - input_b = input_binop->GetLeastConstantLeft(); - op_kind = HInstruction::kAdd; - } - } else { - DCHECK(input_binop->IsSub()); - if (input_binop->GetRight()->IsConstant() && - input_binop->GetRight()->AsConstant()->IsMinusOne()) { - // Interpret - // a * (b - (-1)) - // as - // a + (a * b) - input_b = input_binop->GetLeft(); - op_kind = HInstruction::kAdd; - } else if (input_binop->GetLeft()->IsConstant() && - input_binop->GetLeft()->AsConstant()->IsOne()) { - // Interpret - // a * (1 - b) - // as - // a - (a * b) - input_b = input_binop->GetRight(); - op_kind = HInstruction::kSub; - } - } - - if (input_b == nullptr) { - // We did not find a pattern we can optimize. - return false; - } - - HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate( - mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); - - mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); - input_binop->GetBlock()->RemoveInstruction(input_binop); - - return false; -} - void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { TryExtractArrayAccessAddress(instruction, instruction->GetArray(), @@ -255,75 +195,8 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { - Primitive::Type type = instruction->GetType(); - if (!Primitive::IsIntOrLongType(type)) { - return; - } - - HInstruction* use = instruction->HasNonEnvironmentUses() - ? instruction->GetUses().GetFirst()->GetUser() - : nullptr; - - if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) { - // Replace code looking like - // MUL tmp, x, y - // SUB dst, acc, tmp - // with - // MULSUB dst, acc, x, y - // Note that we do not want to (unconditionally) perform the merge when the - // multiplication has multiple uses and it can be merged in all of them. - // Multiple uses could happen on the same control-flow path, and we would - // then increase the amount of work. In the future we could try to evaluate - // whether all uses are on different control-flow paths (using dominance and - // reverse-dominance information) and only perform the merge when they are. - HInstruction* accumulator = nullptr; - HBinaryOperation* binop = use->AsBinaryOperation(); - HInstruction* binop_left = binop->GetLeft(); - HInstruction* binop_right = binop->GetRight(); - // Be careful after GVN. This should not happen since the `HMul` has only - // one use. - DCHECK_NE(binop_left, binop_right); - if (binop_right == instruction) { - accumulator = binop_left; - } else if (use->IsAdd()) { - DCHECK_EQ(binop_left, instruction); - accumulator = binop_right; - } - - if (accumulator != nullptr) { - HArm64MultiplyAccumulate* mulacc = - new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type, - binop->GetKind(), - accumulator, - instruction->GetLeft(), - instruction->GetRight()); - - binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); - DCHECK(!instruction->HasUses()); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } - } - - // Use multiply accumulate instruction for a few simple patterns. - // We prefer not applying the following transformations if the left and - // right inputs perform the same operation. - // We rely on GVN having squashed the inputs if appropriate. However the - // results are still correct even if that did not happen. - if (instruction->GetLeft() == instruction->GetRight()) { - return; - } - - HInstruction* left = instruction->GetLeft(); - HInstruction* right = instruction->GetRight(); - if ((right->IsAdd() || right->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) { - return; - } - if ((left->IsAdd() || left->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) { - return; + if (TryCombineMultiplyAccumulate(instruction, kArm64)) { + RecordSimplification(); } } diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index b7f490bb8c..37a34c0373 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,10 +51,6 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } - bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, - HBinaryOperation* input_binop, - HInstruction* input_other); - // HInstruction visitors, sorted alphabetically. void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc new file mode 100644 index 0000000000..45d196fa6d --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_shared.h" + +namespace art { + +namespace { + +bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, + HBinaryOperation* input_binop, + HInstruction* input_other) { + DCHECK(Primitive::IsIntOrLongType(mul->GetType())); + DCHECK(input_binop->IsAdd() || input_binop->IsSub()); + DCHECK_NE(input_binop, input_other); + if (!input_binop->HasOnlyOneNonEnvironmentUse()) { + return false; + } + + // Try to interpret patterns like + // a * (b <+/-> 1) + // as + // (a * b) <+/-> a + HInstruction* input_a = input_other; + HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. + HInstruction::InstructionKind op_kind; + + if (input_binop->IsAdd()) { + if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { + // Interpret + // a * (b + 1) + // as + // (a * b) + a + input_b = input_binop->GetLeastConstantLeft(); + op_kind = HInstruction::kAdd; + } + } else { + DCHECK(input_binop->IsSub()); + if (input_binop->GetRight()->IsConstant() && + input_binop->GetRight()->AsConstant()->IsMinusOne()) { + // Interpret + // a * (b - (-1)) + // as + // a + (a * b) + input_b = input_binop->GetLeft(); + op_kind = HInstruction::kAdd; + } else if (input_binop->GetLeft()->IsConstant() && + input_binop->GetLeft()->AsConstant()->IsOne()) { + // Interpret + // a * (1 - b) + // as + // a - (a * b) + input_b = input_binop->GetRight(); + op_kind = HInstruction::kSub; + } + } + + if (input_b == nullptr) { + // We did not find a pattern we can optimize. + return false; + } + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate( + mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); + + mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); + input_binop->GetBlock()->RemoveInstruction(input_binop); + + return true; +} + +} // namespace + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) { + Primitive::Type type = mul->GetType(); + switch (isa) { + case kArm: + case kThumb2: + if (type != Primitive::kPrimInt) { + return false; + } + break; + case kArm64: + if (!Primitive::IsIntOrLongType(type)) { + return false; + } + break; + default: + return false; + } + + HInstruction* use = mul->HasNonEnvironmentUses() + ? mul->GetUses().GetFirst()->GetUser() + : nullptr; + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + + if (mul->HasOnlyOneNonEnvironmentUse()) { + if (use->IsAdd() || use->IsSub()) { + // Replace code looking like + // MUL tmp, x, y + // SUB dst, acc, tmp + // with + // MULSUB dst, acc, x, y + // Note that we do not want to (unconditionally) perform the merge when the + // multiplication has multiple uses and it can be merged in all of them. + // Multiple uses could happen on the same control-flow path, and we would + // then increase the amount of work. In the future we could try to evaluate + // whether all uses are on different control-flow paths (using dominance and + // reverse-dominance information) and only perform the merge when they are. + HInstruction* accumulator = nullptr; + HBinaryOperation* binop = use->AsBinaryOperation(); + HInstruction* binop_left = binop->GetLeft(); + HInstruction* binop_right = binop->GetRight(); + // Be careful after GVN. This should not happen since the `HMul` has only + // one use. + DCHECK_NE(binop_left, binop_right); + if (binop_right == mul) { + accumulator = binop_left; + } else if (use->IsAdd()) { + DCHECK_EQ(binop_left, mul); + accumulator = binop_right; + } + + if (accumulator != nullptr) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + binop->GetKind(), + accumulator, + mul->GetLeft(), + mul->GetRight()); + + binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } else if (use->IsNeg() && isa != kArm) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + HInstruction::kSub, + mul->GetBlock()->GetGraph()->GetConstant(type, 0), + mul->GetLeft(), + mul->GetRight()); + + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } + + // Use multiply accumulate instruction for a few simple patterns. + // We prefer not applying the following transformations if the left and + // right inputs perform the same operation. + // We rely on GVN having squashed the inputs if appropriate. However the + // results are still correct even if that did not happen. + if (mul->GetLeft() == mul->GetRight()) { + return false; + } + + HInstruction* left = mul->GetLeft(); + HInstruction* right = mul->GetRight(); + if ((right->IsAdd() || right->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) { + return true; + } + if ((left->IsAdd() || left->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) { + return true; + } + return false; +} + +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h new file mode 100644 index 0000000000..9832ecc058 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ + +#include "nodes.h" + +namespace art { + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 019be5d494..57fa558129 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1226,6 +1226,16 @@ class HLoopInformationOutwardIterator : public ValueObject { M(UShr, BinaryOperation) \ M(Xor, BinaryOperation) \ +/* + * Instructions, shared across several (not all) architectures. + */ +#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64) +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) +#else +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ + M(MultiplyAccumulate, Instruction) +#endif + #ifndef ART_ENABLE_CODEGEN_arm #define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) #else @@ -1238,8 +1248,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ M(Arm64DataProcWithShifterOp, Instruction) \ - M(Arm64IntermediateAddress, Instruction) \ - M(Arm64MultiplyAccumulate, Instruction) + M(Arm64IntermediateAddress, Instruction) #endif #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -1259,6 +1268,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \ @@ -5718,6 +5728,9 @@ class HParallelMove : public HTemplateInstruction<0> { } // namespace art +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) +#include "nodes_shared.h" +#endif #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 445cdab191..173852a55d 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,40 +118,6 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; -class HArm64MultiplyAccumulate : public HExpression<3> { - public: - HArm64MultiplyAccumulate(Primitive::Type type, - InstructionKind op, - HInstruction* accumulator, - HInstruction* mul_left, - HInstruction* mul_right, - uint32_t dex_pc = kNoDexPc) - : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { - SetRawInputAt(kInputAccumulatorIndex, accumulator); - SetRawInputAt(kInputMulLeftIndex, mul_left); - SetRawInputAt(kInputMulRightIndex, mul_right); - } - - static constexpr int kInputAccumulatorIndex = 0; - static constexpr int kInputMulLeftIndex = 1; - static constexpr int kInputMulRightIndex = 2; - - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_; - } - - InstructionKind GetOpKind() const { return op_kind_; } - - DECLARE_INSTRUCTION(Arm64MultiplyAccumulate); - - private: - // Indicates if this is a MADD or MSUB. - InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate); -}; - } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h new file mode 100644 index 0000000000..b04b622838 --- /dev/null +++ b/compiler/optimizing/nodes_shared.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ + +namespace art { + +class HMultiplyAccumulate : public HExpression<3> { + public: + HMultiplyAccumulate(Primitive::Type type, + InstructionKind op, + HInstruction* accumulator, + HInstruction* mul_left, + HInstruction* mul_right, + uint32_t dex_pc = kNoDexPc) + : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { + SetRawInputAt(kInputAccumulatorIndex, accumulator); + SetRawInputAt(kInputMulLeftIndex, mul_left); + SetRawInputAt(kInputMulRightIndex, mul_right); + } + + static constexpr int kInputAccumulatorIndex = 0; + static constexpr int kInputMulLeftIndex = 1; + static constexpr int kInputMulRightIndex = 2; + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return op_kind_ == other->AsMultiplyAccumulate()->op_kind_; + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(MultiplyAccumulate); + + private: + // Indicates if this is a MADD or MSUB. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index fffd00535c..4da48bdfc3 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -62,6 +62,7 @@ #include "induction_var_analysis.h" #include "inliner.h" #include "instruction_simplifier.h" +#include "instruction_simplifier_arm.h" #include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit_code_cache.h" @@ -445,8 +446,11 @@ static void RunArchOptimizations(InstructionSet instruction_set, case kThumb2: case kArm: { arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats); + arm::InstructionSimplifierArm* simplifier = + new (arena) arm::InstructionSimplifierArm(graph, stats); HOptimization* arm_optimizations[] = { - fixups + fixups, + simplifier }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); break; diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index b6a228c13c..e57a540669 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -21,6 +21,7 @@ #include <set> #include <map> #include <vector> +#include <zlib.h> #include "base/bit_utils.h" #include "base/logging.h" @@ -161,7 +162,6 @@ class TestDexFileBuilder { uint32_t total_size = data_section_offset + data_section_size; dex_file_data_.resize(total_size); - std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); for (const auto& entry : strings_) { CHECK_LT(entry.first.size(), 128u); @@ -210,7 +210,12 @@ class TestDexFileBuilder { Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); } - // Leave checksum and signature as zeros. + // Leave signature as zeros. + + header->file_size_ = dex_file_data_.size(); + size_t skip = sizeof(header->magic_) + sizeof(header->checksum_); + header->checksum_ = adler32(0u, dex_file_data_.data() + skip, dex_file_data_.size() - skip); + std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); std::string error_msg; std::unique_ptr<const DexFile> dex_file(DexFile::Open( |