diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/oat_writer.cc | 1250 | ||||
| -rw-r--r-- | compiler/oat_writer.h | 101 |
2 files changed, 747 insertions, 604 deletions
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index dc66e9c108..2114fe921d 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -38,6 +38,14 @@ namespace art { +#define DCHECK_OFFSET() \ + DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \ + << "file_offset=" << file_offset << " relative_offset=" << relative_offset + +#define DCHECK_OFFSET_() \ + 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, @@ -66,7 +74,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_quick_resolution_trampoline_(0), size_quick_to_interpreter_bridge_(0), size_trampoline_alignment_(0), - size_code_size_(0), + size_method_header_(0), size_code_(0), size_code_alignment_(0), size_mapping_table_(0), @@ -99,6 +107,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, offset = InitOatClasses(offset); } { + TimingLogger::ScopedSplit split("InitOatMaps", timings); + offset = InitOatMaps(offset); + } + { TimingLogger::ScopedSplit split("InitOatCode", timings); offset = InitOatCode(offset); } @@ -118,6 +130,585 @@ OatWriter::~OatWriter() { STLDeleteElements(&oat_classes_); } +struct OatWriter::GcMapDataAccess { + static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + return &compiled_method->GetGcMap(); + } + + static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { + return oat_class->method_offsets_[method_offsets_index].gc_map_offset_; + } + + static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) + ALWAYS_INLINE { + oat_class->method_offsets_[method_offsets_index].gc_map_offset_ = offset; + } + + static const char* Name() ALWAYS_INLINE { + return "GC map"; + } +}; + +struct OatWriter::MappingTableDataAccess { + static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + return &compiled_method->GetMappingTable(); + } + + static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { + return oat_class->method_offsets_[method_offsets_index].mapping_table_offset_; + } + + static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) + ALWAYS_INLINE { + oat_class->method_offsets_[method_offsets_index].mapping_table_offset_ = offset; + } + + static const char* Name() ALWAYS_INLINE { + return "mapping table"; + } +}; + +struct OatWriter::VmapTableDataAccess { + static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + return &compiled_method->GetVmapTable(); + } + + static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { + return oat_class->method_offsets_[method_offsets_index].vmap_table_offset_; + } + + static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) + ALWAYS_INLINE { + oat_class->method_offsets_[method_offsets_index].vmap_table_offset_ = offset; + } + + static const char* Name() ALWAYS_INLINE { + return "vmap table"; + } +}; + +class OatWriter::DexMethodVisitor { + public: + DexMethodVisitor(OatWriter* writer, size_t offset) + : writer_(writer), + offset_(offset), + dex_file_(nullptr), + class_def_index_(DexFile::kDexNoIndex) { + } + + virtual bool StartClass(const DexFile* dex_file, size_t class_def_index) { + DCHECK(dex_file_ == nullptr); + DCHECK_EQ(class_def_index_, DexFile::kDexNoIndex); + dex_file_ = dex_file; + class_def_index_ = class_def_index; + return true; + } + + virtual bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) = 0; + + virtual bool EndClass() { + if (kIsDebugBuild) { + dex_file_ = nullptr; + class_def_index_ = DexFile::kDexNoIndex; + } + return true; + } + + size_t GetOffset() const { + return offset_; + } + + protected: + virtual ~DexMethodVisitor() { } + + OatWriter* const writer_; + + // The offset is usually advanced for each visited method by the derived class. + size_t offset_; + + // The dex file and class def index are set in StartClass(). + const DexFile* dex_file_; + size_t class_def_index_; +}; + +class OatWriter::OatDexMethodVisitor : public DexMethodVisitor { + public: + OatDexMethodVisitor(OatWriter* writer, size_t offset) + : DexMethodVisitor(writer, offset), + oat_class_index_(0u), + method_offsets_index_(0u) { + } + + bool StartClass(const DexFile* dex_file, size_t class_def_index) { + DexMethodVisitor::StartClass(dex_file, class_def_index); + DCHECK_LT(oat_class_index_, writer_->oat_classes_.size()); + method_offsets_index_ = 0u; + return true; + } + + bool EndClass() { + ++oat_class_index_; + return DexMethodVisitor::EndClass(); + } + + protected: + size_t oat_class_index_; + size_t method_offsets_index_; +}; + +class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { + public: + InitOatClassesMethodVisitor(OatWriter* writer, size_t offset) + : DexMethodVisitor(writer, offset), + compiled_methods_(), + num_non_null_compiled_methods_(0u) { + compiled_methods_.reserve(256u); + } + + bool StartClass(const DexFile* dex_file, size_t class_def_index) { + DexMethodVisitor::StartClass(dex_file, class_def_index); + compiled_methods_.clear(); + num_non_null_compiled_methods_ = 0u; + return true; + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) { + // Fill in the compiled_methods_ array for methods that have a + // CompiledMethod. We track the number of non-null entries in + // num_non_null_compiled_methods_ since we only want to allocate + // OatMethodOffsets for the compiled methods. + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); + compiled_methods_.push_back(compiled_method); + if (compiled_method != nullptr) { + ++num_non_null_compiled_methods_; + } + return true; + } + + bool EndClass() { + ClassReference class_ref(dex_file_, class_def_index_); + CompiledClass* compiled_class = writer_->compiler_driver_->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + + OatClass* oat_class = new OatClass(offset_, compiled_methods_, + num_non_null_compiled_methods_, status); + writer_->oat_classes_.push_back(oat_class); + offset_ += oat_class->SizeOf(); + return DexMethodVisitor::EndClass(); + } + + private: + std::vector<CompiledMethod*> compiled_methods_; + size_t num_non_null_compiled_methods_; +}; + +class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { + public: + InitCodeMethodVisitor(OatWriter* writer, size_t offset) + : OatDexMethodVisitor(writer, offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != nullptr) { + // Derived from CompiledMethod. + uint32_t quick_code_offset = 0; + uint32_t frame_size_in_bytes = kStackAlignment; + uint32_t core_spill_mask = 0; + uint32_t fp_spill_mask = 0; + + const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode(); + const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); + if (portable_code != nullptr) { + CHECK(quick_code == nullptr); + size_t oat_method_offsets_offset = + oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index); + compiled_method->AddOatdataOffsetToCompliledCodeOffset( + oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_)); + } else { + CHECK(quick_code != nullptr); + offset_ = compiled_method->AlignCode(offset_); + DCHECK_ALIGNED_PARAM(offset_, + GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + uint32_t code_size = quick_code->size() * sizeof(uint8_t); + CHECK_NE(code_size, 0U); + uint32_t thumb_offset = compiled_method->CodeDelta(); + quick_code_offset = offset_ + sizeof(OatMethodHeader) + thumb_offset; + + std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation(); + if (cfi_info != nullptr) { + // Copy in the FDE, if present + const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo(); + if (fde != nullptr) { + // Copy the information into cfi_info and then fix the address in the new copy. + int cur_offset = cfi_info->size(); + cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); + + // Set the 'initial_location' field to address the start of the method. + uint32_t new_value = quick_code_offset - writer_->oat_header_->GetExecutableOffset(); + uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); + (*cfi_info)[offset_to_update+0] = new_value; + (*cfi_info)[offset_to_update+1] = new_value >> 8; + (*cfi_info)[offset_to_update+2] = new_value >> 16; + (*cfi_info)[offset_to_update+3] = new_value >> 24; + std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, false); + writer_->method_info_.push_back(DebugInfo(name, new_value, new_value + code_size)); + } + } + + // Deduplicate code arrays. + auto code_iter = dedupe_map_.find(quick_code); + if (code_iter != dedupe_map_.end()) { + quick_code_offset = code_iter->second; + } else { + dedupe_map_.Put(quick_code, quick_code_offset); + OatMethodHeader method_header(code_size); + offset_ += sizeof(method_header); // Method header is prepended before code. + writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header)); + offset_ += code_size; + writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size); + } + } + frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); + core_spill_mask = compiled_method->GetCoreSpillMask(); + fp_spill_mask = compiled_method->GetFpSpillMask(); + + if (kIsDebugBuild) { + // We expect GC maps except when the class hasn't been verified or the method is native. + const CompilerDriver* compiler_driver = writer_->compiler_driver_; + ClassReference class_ref(dex_file_, class_def_index_); + CompiledClass* compiled_class = compiler_driver->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (compiler_driver->GetVerificationResults()->IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); + size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); + bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; + CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) + << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " + << (status < mirror::Class::kStatusVerified) << " " << status << " " + << PrettyMethod(it.GetMemberIndex(), *dex_file_); + } + + DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); + OatMethodOffsets* offsets = &oat_class->method_offsets_[method_offsets_index_]; + offsets->code_offset_ = quick_code_offset; + offsets->frame_size_in_bytes_ = frame_size_in_bytes; + offsets->core_spill_mask_ = core_spill_mask; + offsets->fp_spill_mask_ = fp_spill_mask; + ++method_offsets_index_; + } + + return true; + } + + private: + // Deduplication is already done on a pointer basis by the compiler driver, + // so we can simply compare the pointers to find out if things are duplicated. + SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_; +}; + +template <typename DataAccess> +class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { + public: + InitMapMethodVisitor(OatWriter* writer, size_t offset) + : OatDexMethodVisitor(writer, offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != nullptr) { + DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); + DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u); + + const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method); + uint32_t map_size = map->size() * sizeof((*map)[0]); + if (map_size != 0u) { + auto it = dedupe_map_.find(map); + if (it != dedupe_map_.end()) { + DataAccess::SetOffset(oat_class, method_offsets_index_, it->second); + } else { + DataAccess::SetOffset(oat_class, method_offsets_index_, offset_); + dedupe_map_.Put(map, offset_); + offset_ += map_size; + writer_->oat_header_->UpdateChecksum(&(*map)[0], map_size); + } + } + ++method_offsets_index_; + } + + return true; + } + + private: + // Deduplication is already done on a pointer basis by the compiler driver, + // so we can simply compare the pointers to find out if things are duplicated. + SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_; +}; + +class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { + public: + InitImageMethodVisitor(OatWriter* writer, size_t offset) + : OatDexMethodVisitor(writer, offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u, 0u, 0u); + if (compiled_method != nullptr) { + DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); + offsets = oat_class->method_offsets_[method_offsets_index_]; + ++method_offsets_index_; + } + + // Derive frame size and spill masks for native methods without code: + // These are generic JNI methods... + uint32_t method_idx = it.GetMemberIndex(); + bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; + if (is_native && compiled_method == nullptr) { + // Compute Sirt size as putting _every_ reference into it, even null ones. + uint32_t s_len; + const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx), + &s_len); + DCHECK(shorty != nullptr); + uint32_t refs = 1; // Native method always has "this" or class. + for (uint32_t i = 1; i < s_len; ++i) { + if (shorty[i] == 'L') { + refs++; + } + } + size_t pointer_size = GetInstructionSetPointerSize( + writer_->compiler_driver_->GetInstructionSet()); + size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs); + + // Get the generic spill masks and base frame size. + mirror::ArtMethod* callee_save_method = + Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs); + + offsets.frame_size_in_bytes_ = callee_save_method->GetFrameSizeInBytes() + sirt_size; + offsets.core_spill_mask_ = callee_save_method->GetCoreSpillMask(); + offsets.fp_spill_mask_ = callee_save_method->GetFpSpillMask(); + DCHECK_EQ(offsets.mapping_table_offset_, 0u); + DCHECK_EQ(offsets.vmap_table_offset_, 0u); + DCHECK_EQ(offsets.gc_map_offset_, 0u); + } + + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_)); + // Unchecked as we hold mutator_lock_ on entry. + ScopedObjectAccessUnchecked soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(*dex_file_)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); + mirror::ArtMethod* method = linker->ResolveMethod(*dex_file_, method_idx, dex_cache, + class_loader, nullptr, invoke_type); + CHECK(method != NULL); + method->SetFrameSizeInBytes(offsets.frame_size_in_bytes_); + method->SetCoreSpillMask(offsets.core_spill_mask_); + method->SetFpSpillMask(offsets.fp_spill_mask_); + method->SetOatMappingTableOffset(offsets.mapping_table_offset_); + // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking. + method->SetQuickOatCodeOffset(offsets.code_offset_); + method->SetOatVmapTableOffset(offsets.vmap_table_offset_); + method->SetOatNativeGcMapOffset(offsets.gc_map_offset_); + + return true; + } +}; + +class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { + public: + WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, + size_t relative_offset) + : OatDexMethodVisitor(writer, relative_offset), + out_(out), + file_offset_(file_offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != NULL) { // ie. not an abstract method + size_t file_offset = file_offset_; + OutputStream* out = out_; + + const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); + if (quick_code != nullptr) { + CHECK(compiled_method->GetPortableCode() == nullptr); + uint32_t aligned_offset = compiled_method->AlignCode(offset_); + uint32_t aligned_code_delta = aligned_offset - offset_; + if (aligned_code_delta != 0) { + static const uint8_t kPadding[] = { + 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u + }; + DCHECK_LE(aligned_code_delta, sizeof(kPadding)); + if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) { + ReportWriteFailure("code alignment padding", it); + return false; + } + writer_->size_code_alignment_ += aligned_code_delta; + offset_ += aligned_code_delta; + DCHECK_OFFSET_(); + } + DCHECK_ALIGNED_PARAM(offset_, + GetInstructionSetAlignment(compiled_method->GetInstructionSet())); + uint32_t code_size = quick_code->size() * sizeof(uint8_t); + CHECK_NE(code_size, 0U); + + // Deduplicate code arrays. + const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_]; + DCHECK(method_offsets.code_offset_ < offset_ || method_offsets.code_offset_ == + offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta()) + << PrettyMethod(it.GetMemberIndex(), *dex_file_); + if (method_offsets.code_offset_ >= offset_) { + OatMethodHeader method_header(code_size); + if (!out->WriteFully(&method_header, sizeof(method_header))) { + ReportWriteFailure("method header", it); + return false; + } + writer_->size_method_header_ += sizeof(method_header); + offset_ += sizeof(method_header); + DCHECK_OFFSET_(); + if (!out->WriteFully(&(*quick_code)[0], code_size)) { + ReportWriteFailure("method code", it); + return false; + } + writer_->size_code_ += code_size; + offset_ += code_size; + } + DCHECK_OFFSET_(); + } + ++method_offsets_index_; + } + + return true; + } + + private: + OutputStream* const out_; + size_t const file_offset_; + + void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) { + PLOG(ERROR) << "Failed to write " << what << " for " + << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation(); + } +}; + +template <typename DataAccess> +class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { + public: + WriteMapMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, + size_t relative_offset) + : OatDexMethodVisitor(writer, relative_offset), + out_(out), + file_offset_(file_offset) { + } + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) { + OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; + const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); + + if (compiled_method != NULL) { // ie. not an abstract method + size_t file_offset = file_offset_; + OutputStream* out = out_; + + uint32_t map_offset = DataAccess::GetOffset(oat_class, method_offsets_index_); + ++method_offsets_index_; + + // Write deduplicated map. + const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method); + size_t map_size = map->size() * sizeof((*map)[0]); + DCHECK((map_size == 0u && map_offset == 0u) || + (map_size != 0u && map_offset != 0u && map_offset <= offset_)) + << PrettyMethod(it.GetMemberIndex(), *dex_file_); + if (map_size != 0u && map_offset == offset_) { + if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) { + ReportWriteFailure(it); + return false; + } + offset_ += map_size; + } + DCHECK_OFFSET_(); + } + + return true; + } + + private: + OutputStream* const out_; + size_t const file_offset_; + + void ReportWriteFailure(const ClassDataItemIterator& it) { + PLOG(ERROR) << "Failed to write " << DataAccess::Name() << " for " + << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation(); + } +}; + +// Visit all methods from all classes in all dex files with the specified visitor. +bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { + for (const DexFile* dex_file : *dex_files_) { + const size_t class_def_count = dex_file->NumClassDefs(); + for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { + if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) { + return false; + } + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + const byte* class_data = dex_file->GetClassData(class_def); + if (class_data != NULL) { // ie not an empty class, such as a marker interface + ClassDataItemIterator it(*dex_file, class_data); + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + size_t class_def_method_index = 0u; + while (it.HasNextDirectMethod()) { + if (!visitor->VisitMethod(class_def_method_index, it)) { + return false; + } + ++class_def_method_index; + it.Next(); + } + while (it.HasNextVirtualMethod()) { + if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { + return false; + } + ++class_def_method_index; + it.Next(); + } + } + if (UNLIKELY(!visitor->EndClass())) { + return false; + } + } + } + return true; +} + size_t OatWriter::InitOatHeader() { // create the OatHeader oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(), @@ -161,78 +752,42 @@ size_t OatWriter::InitDexFiles(size_t offset) { } size_t OatWriter::InitOatClasses(size_t offset) { - // create the OatClasses // calculate the offsets within OatDexFiles to OatClasses - for (size_t i = 0; i != dex_files_->size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - for (size_t class_def_index = 0; - class_def_index < dex_file->NumClassDefs(); - class_def_index++) { - oat_dex_files_[i]->methods_offsets_[class_def_index] = offset; - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const byte* class_data = dex_file->GetClassData(class_def); - uint32_t num_non_null_compiled_methods = 0; - UniquePtr<std::vector<CompiledMethod*> > compiled_methods(new std::vector<CompiledMethod*>()); - if (class_data != NULL) { // ie not an empty class, such as a marker interface - ClassDataItemIterator it(*dex_file, class_data); - size_t num_direct_methods = it.NumDirectMethods(); - size_t num_virtual_methods = it.NumVirtualMethods(); - size_t num_methods = num_direct_methods + num_virtual_methods; - - // Fill in the compiled_methods_ array for methods that have a - // CompiledMethod. We track the number of non-null entries in - // num_non_null_compiled_methods since we only want to allocate - // OatMethodOffsets for the compiled methods. - compiled_methods->reserve(num_methods); - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - size_t class_def_method_index = 0; - while (it.HasNextDirectMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); - compiled_methods->push_back(compiled_method); - if (compiled_method != NULL) { - num_non_null_compiled_methods++; - } - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx)); - compiled_methods->push_back(compiled_method); - if (compiled_method != NULL) { - num_non_null_compiled_methods++; - } - class_def_method_index++; - it.Next(); - } - } - - ClassReference class_ref(dex_file, class_def_index); - CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); - mirror::Class::Status status; - if (compiled_class != NULL) { - status = compiled_class->GetStatus(); - } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; - } else { - status = mirror::Class::kStatusNotReady; - } - - OatClass* oat_class = new OatClass(offset, compiled_methods.release(), - num_non_null_compiled_methods, status); - oat_classes_.push_back(oat_class); - offset += oat_class->SizeOf(); + InitOatClassesMethodVisitor visitor(this, offset); + bool success = VisitDexMethods(&visitor); + CHECK(success); + offset = visitor.GetOffset(); + + // Update oat_dex_files_. + auto oat_class_it = oat_classes_.begin(); + for (OatDexFile* oat_dex_file : oat_dex_files_) { + for (uint32_t& offset : oat_dex_file->methods_offsets_) { + DCHECK(oat_class_it != oat_classes_.end()); + offset = (*oat_class_it)->offset_; + ++oat_class_it; } - oat_dex_files_[i]->UpdateChecksum(oat_header_); + oat_dex_file->UpdateChecksum(oat_header_); } + CHECK(oat_class_it == oat_classes_.end()); + + return offset; +} + +size_t OatWriter::InitOatMaps(size_t offset) { + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, offset); \ + bool success = VisitDexMethods(&visitor); \ + DCHECK(success); \ + offset = visitor.GetOffset(); \ + } while (false) + + VISIT(InitMapMethodVisitor<GcMapDataAccess>); + VISIT(InitMapMethodVisitor<MappingTableDataAccess>); + VISIT(InitMapMethodVisitor<VmapTableDataAccess>); + + #undef VISIT + return offset; } @@ -280,280 +835,24 @@ size_t OatWriter::InitOatCode(size_t offset) { } size_t OatWriter::InitOatCodeDexFiles(size_t offset) { - size_t oat_class_index = 0; - for (size_t i = 0; i != dex_files_->size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - CHECK(dex_file != NULL); - offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file); - } - return offset; -} - -size_t OatWriter::InitOatCodeDexFile(size_t offset, - size_t* oat_class_index, - const DexFile& dex_file) { - for (size_t class_def_index = 0; - class_def_index < dex_file.NumClassDefs(); - class_def_index++, (*oat_class_index)++) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def); - oat_classes_[*oat_class_index]->UpdateChecksum(oat_header_); - } - return offset; -} - -size_t OatWriter::InitOatCodeClassDef(size_t offset, - size_t oat_class_index, size_t class_def_index, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) { - const byte* class_data = dex_file.GetClassData(class_def); - if (class_data == NULL) { - // empty class, such as a marker interface - return offset; - } - ClassDataItemIterator it(dex_file, class_data); - CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(), - it.NumDirectMethods() + it.NumVirtualMethods()); - // Skip fields - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - // Process methods - size_t class_def_method_index = 0; - size_t method_offsets_index = 0; - while (it.HasNextDirectMethod()) { - bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; - offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, - &method_offsets_index, is_native, - it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file); - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0; - offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index, - &method_offsets_index, is_native, - it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file); - class_def_method_index++; - it.Next(); - } - DCHECK(!it.HasNext()); - CHECK_LE(method_offsets_index, class_def_method_index); - return offset; -} - -size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, - size_t __attribute__((unused)) class_def_index, - size_t class_def_method_index, - size_t* method_offsets_index, - bool __attribute__((unused)) is_native, - InvokeType invoke_type, - uint32_t method_idx, const DexFile& dex_file) { - // Derived from CompiledMethod if available. - uint32_t quick_code_offset = 0; - uint32_t frame_size_in_bytes = kStackAlignment; - uint32_t core_spill_mask = 0; - uint32_t fp_spill_mask = 0; - uint32_t mapping_table_offset = 0; - uint32_t vmap_table_offset = 0; - uint32_t gc_map_offset = 0; - - OatClass* oat_class = oat_classes_[oat_class_index]; - CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (compiled_method != nullptr) { - const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode(); - const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - if (portable_code != nullptr) { - CHECK(quick_code == nullptr); - size_t oat_method_offsets_offset = - oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index); - compiled_method->AddOatdataOffsetToCompliledCodeOffset( - oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_)); - } else { - CHECK(quick_code != nullptr); - offset = compiled_method->AlignCode(offset); - DCHECK_ALIGNED_PARAM(offset, - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - - uint32_t code_size = quick_code->size() * sizeof(uint8_t); - CHECK_NE(code_size, 0U); - uint32_t thumb_offset = compiled_method->CodeDelta(); - quick_code_offset = offset + sizeof(code_size) + thumb_offset; - - std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation(); - if (cfi_info != nullptr) { - // Copy in the FDE, if present - const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo(); - if (fde != nullptr) { - // Copy the information into cfi_info and then fix the address in the new copy. - int cur_offset = cfi_info->size(); - cfi_info->insert(cfi_info->end(), fde->begin(), fde->end()); - - // Set the 'initial_location' field to address the start of the method. - uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset(); - uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t); - (*cfi_info)[offset_to_update+0] = new_value; - (*cfi_info)[offset_to_update+1] = new_value >> 8; - (*cfi_info)[offset_to_update+2] = new_value >> 16; - (*cfi_info)[offset_to_update+3] = new_value >> 24; - method_info_.push_back(DebugInfo(PrettyMethod(method_idx, dex_file, false), - new_value, new_value + code_size)); - } - } - - // Deduplicate code arrays - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = - code_offsets_.find(quick_code); - if (code_iter != code_offsets_.end()) { - quick_code_offset = code_iter->second; - } else { - code_offsets_.Put(quick_code, quick_code_offset); - offset += sizeof(code_size); // code size is prepended before code - offset += code_size; - oat_header_->UpdateChecksum(&(*quick_code)[0], code_size); - } - } - frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); - core_spill_mask = compiled_method->GetCoreSpillMask(); - fp_spill_mask = compiled_method->GetFpSpillMask(); - - const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); - size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]); - mapping_table_offset = (mapping_table_size == 0) ? 0 : offset; - - // Deduplicate mapping tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter = - mapping_table_offsets_.find(&mapping_table); - if (mapping_iter != mapping_table_offsets_.end()) { - mapping_table_offset = mapping_iter->second; - } else { - mapping_table_offsets_.Put(&mapping_table, mapping_table_offset); - offset += mapping_table_size; - oat_header_->UpdateChecksum(&mapping_table[0], mapping_table_size); - } - - const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); - size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]); - vmap_table_offset = (vmap_table_size == 0) ? 0 : offset; - - // Deduplicate vmap tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter = - vmap_table_offsets_.find(&vmap_table); - if (vmap_iter != vmap_table_offsets_.end()) { - vmap_table_offset = vmap_iter->second; - } else { - vmap_table_offsets_.Put(&vmap_table, vmap_table_offset); - offset += vmap_table_size; - oat_header_->UpdateChecksum(&vmap_table[0], vmap_table_size); - } - - const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); - size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); - gc_map_offset = (gc_map_size == 0) ? 0 : offset; - - if (kIsDebugBuild) { - // We expect GC maps except when the class hasn't been verified or the method is native - ClassReference class_ref(&dex_file, class_def_index); - CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); - mirror::Class::Status status; - if (compiled_class != NULL) { - status = compiled_class->GetStatus(); - } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; - } else { - status = mirror::Class::kStatusNotReady; - } - CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) - << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " - << (status < mirror::Class::kStatusVerified) << " " << status << " " - << PrettyMethod(method_idx, dex_file); - } - - // Deduplicate GC maps - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = - gc_map_offsets_.find(&gc_map); - if (gc_map_iter != gc_map_offsets_.end()) { - gc_map_offset = gc_map_iter->second; - } else { - gc_map_offsets_.Put(&gc_map, gc_map_offset); - offset += gc_map_size; - oat_header_->UpdateChecksum(&gc_map[0], gc_map_size); - } - - oat_class->method_offsets_[*method_offsets_index] = - OatMethodOffsets(quick_code_offset, - frame_size_in_bytes, - core_spill_mask, - fp_spill_mask, - mapping_table_offset, - vmap_table_offset, - gc_map_offset); - (*method_offsets_index)++; - } - - + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, offset); \ + bool success = VisitDexMethods(&visitor); \ + DCHECK(success); \ + offset = visitor.GetOffset(); \ + } while (false) + + VISIT(InitCodeMethodVisitor); if (compiler_driver_->IsImage()) { - // Derive frame size and spill masks for native methods without code: - // These are generic JNI methods... - if (is_native && compiled_method == nullptr) { - // Compute Sirt size as putting _every_ reference into it, even null ones. - uint32_t s_len; - const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx), &s_len); - DCHECK(shorty != nullptr); - uint32_t refs = 1; // Native method always has "this" or class. - for (uint32_t i = 1; i < s_len; ++i) { - if (shorty[i] == 'L') { - refs++; - } - } - size_t pointer_size = GetInstructionSetPointerSize(compiler_driver_->GetInstructionSet()); - size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs); - - // Get the generic spill masks and base frame size. - mirror::ArtMethod* callee_save_method = - Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs); - - frame_size_in_bytes = callee_save_method->GetFrameSizeInBytes() + sirt_size; - core_spill_mask = callee_save_method->GetCoreSpillMask(); - fp_spill_mask = callee_save_method->GetFpSpillMask(); - mapping_table_offset = 0; - vmap_table_offset = 0; - gc_map_offset = 0; - } - - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - // Unchecked as we hold mutator_lock_ on entry. - ScopedObjectAccessUnchecked soa(Thread::Current()); - SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(dex_file)); - SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); - mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache, - class_loader, nullptr, invoke_type); - CHECK(method != NULL); - method->SetFrameSizeInBytes(frame_size_in_bytes); - method->SetCoreSpillMask(core_spill_mask); - method->SetFpSpillMask(fp_spill_mask); - method->SetOatMappingTableOffset(mapping_table_offset); - // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking. - method->SetQuickOatCodeOffset(quick_code_offset); - method->SetOatVmapTableOffset(vmap_table_offset); - method->SetOatNativeGcMapOffset(gc_map_offset); + VISIT(InitImageMethodVisitor); } + #undef VISIT + return offset; } -#define DCHECK_OFFSET() \ - DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \ - << "file_offset=" << file_offset << " relative_offset=" << relative_offset - -#define DCHECK_OFFSET_() \ - DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ - << "file_offset=" << file_offset << " offset_=" << offset_ - bool OatWriter::Write(OutputStream* out) { const size_t file_offset = out->Seek(0, kSeekCurrent); @@ -574,7 +873,14 @@ bool OatWriter::Write(OutputStream* out) { return false; } - size_t relative_offset = WriteCode(out, file_offset); + size_t relative_offset = out->Seek(0, kSeekCurrent) - file_offset; + relative_offset = WriteMaps(out, file_offset, relative_offset); + if (relative_offset == 0) { + LOG(ERROR) << "Failed to write oat code to " << out->GetLocation(); + return false; + } + + relative_offset = WriteCode(out, file_offset, relative_offset); if (relative_offset == 0) { LOG(ERROR) << "Failed to write oat code to " << out->GetLocation(); return false; @@ -608,7 +914,7 @@ bool OatWriter::Write(OutputStream* out) { DO_STAT(size_quick_resolution_trampoline_); DO_STAT(size_quick_to_interpreter_bridge_); DO_STAT(size_trampoline_alignment_); - DO_STAT(size_code_size_); + DO_STAT(size_method_header_); DO_STAT(size_code_); DO_STAT(size_code_alignment_); DO_STAT(size_mapping_table_); @@ -669,9 +975,37 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { return true; } -size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) { - size_t relative_offset = oat_header_->GetExecutableOffset(); +size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) { + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, out, file_offset, relative_offset); \ + if (UNLIKELY(!VisitDexMethods(&visitor))) { \ + return 0; \ + } \ + relative_offset = visitor.GetOffset(); \ + } while (false) + + size_t gc_maps_offset = relative_offset; + VISIT(WriteMapMethodVisitor<GcMapDataAccess>); + size_gc_map_ = relative_offset - gc_maps_offset; + + size_t mapping_tables_offset = relative_offset; + VISIT(WriteMapMethodVisitor<MappingTableDataAccess>); + size_mapping_table_ = relative_offset - mapping_tables_offset; + + size_t vmap_tables_offset = relative_offset; + VISIT(WriteMapMethodVisitor<VmapTableDataAccess>); + size_vmap_table_ = relative_offset - vmap_tables_offset; + + #undef VISIT + + return relative_offset; +} + +size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset) { off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent); + relative_offset += size_executable_offset_alignment_; + DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset()); size_t expected_file_offset = file_offset + relative_offset; if (static_cast<uint32_t>(new_offset) != expected_file_offset) { PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset @@ -715,218 +1049,18 @@ size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) { size_t OatWriter::WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset) { - size_t oat_class_index = 0; - for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - CHECK(dex_file != NULL); - relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index, - *dex_file); - if (relative_offset == 0) { - return 0; - } - } - return relative_offset; -} - -size_t OatWriter::WriteCodeDexFile(OutputStream* out, const size_t file_offset, - size_t relative_offset, size_t* oat_class_index, - const DexFile& dex_file) { - for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); - class_def_index++, (*oat_class_index)++) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index, - dex_file, class_def); - if (relative_offset == 0) { - return 0; - } - } - return relative_offset; -} - -void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx, - const DexFile& dex_file, const OutputStream& out) const { - PLOG(ERROR) << "Failed to write " << what << " for " << PrettyMethod(method_idx, dex_file) - << " to " << out.GetLocation(); -} + #define VISIT(VisitorType) \ + do { \ + VisitorType visitor(this, out, file_offset, relative_offset); \ + if (UNLIKELY(!VisitDexMethods(&visitor))) { \ + return 0; \ + } \ + relative_offset = visitor.GetOffset(); \ + } while (false) -size_t OatWriter::WriteCodeClassDef(OutputStream* out, - const size_t file_offset, - size_t relative_offset, - size_t oat_class_index, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) { - const byte* class_data = dex_file.GetClassData(class_def); - if (class_data == NULL) { - // ie. an empty class such as a marker interface - return relative_offset; - } - ClassDataItemIterator it(dex_file, class_data); - // Skip fields - while (it.HasNextStaticField()) { - it.Next(); - } - while (it.HasNextInstanceField()) { - it.Next(); - } - // Process methods - size_t class_def_method_index = 0; - size_t method_offsets_index = 0; - while (it.HasNextDirectMethod()) { - bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0; - relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index, - class_def_method_index, &method_offsets_index, is_static, - it.GetMemberIndex(), dex_file); - if (relative_offset == 0) { - return 0; - } - class_def_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index, - class_def_method_index, &method_offsets_index, false, - it.GetMemberIndex(), dex_file); - if (relative_offset == 0) { - return 0; - } - class_def_method_index++; - it.Next(); - } - DCHECK(!it.HasNext()); - CHECK_LE(method_offsets_index, class_def_method_index); - return relative_offset; -} + VISIT(WriteCodeMethodVisitor); -size_t OatWriter::WriteCodeMethod(OutputStream* out, const size_t file_offset, - size_t relative_offset, size_t oat_class_index, - size_t class_def_method_index, size_t* method_offsets_index, - bool is_static, uint32_t method_idx, const DexFile& dex_file) { - OatClass* oat_class = oat_classes_[oat_class_index]; - const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - - if (compiled_method != NULL) { // ie. not an abstract method - const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index]; - (*method_offsets_index)++; - const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - if (quick_code != nullptr) { - CHECK(compiled_method->GetPortableCode() == nullptr); - uint32_t aligned_offset = compiled_method->AlignCode(relative_offset); - uint32_t aligned_code_delta = aligned_offset - relative_offset; - if (aligned_code_delta != 0) { - off_t new_offset = out->Seek(aligned_code_delta, kSeekCurrent); - size_code_alignment_ += aligned_code_delta; - uint32_t expected_offset = file_offset + aligned_offset; - if (static_cast<uint32_t>(new_offset) != expected_offset) { - PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset - << " Expected: " << expected_offset << " File: " << out->GetLocation(); - return 0; - } - relative_offset += aligned_code_delta; - DCHECK_OFFSET(); - } - DCHECK_ALIGNED_PARAM(relative_offset, - GetInstructionSetAlignment(compiled_method->GetInstructionSet())); - - uint32_t code_size = quick_code->size() * sizeof(uint8_t); - CHECK_NE(code_size, 0U); - - // Deduplicate code arrays - size_t code_offset = relative_offset + sizeof(code_size) + compiled_method->CodeDelta(); - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = - code_offsets_.find(quick_code); - if (code_iter != code_offsets_.end() && code_offset != method_offsets.code_offset_) { - DCHECK(code_iter->second == method_offsets.code_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK(code_offset == method_offsets.code_offset_) << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&code_size, sizeof(code_size))) { - ReportWriteFailure("method code size", method_idx, dex_file, *out); - return 0; - } - size_code_size_ += sizeof(code_size); - relative_offset += sizeof(code_size); - DCHECK_OFFSET(); - if (!out->WriteFully(&(*quick_code)[0], code_size)) { - ReportWriteFailure("method code", method_idx, dex_file, *out); - return 0; - } - size_code_ += code_size; - relative_offset += code_size; - } - DCHECK_OFFSET(); - } - const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); - size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]); - - // Deduplicate mapping tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter = - mapping_table_offsets_.find(&mapping_table); - if (mapping_iter != mapping_table_offsets_.end() && - relative_offset != method_offsets.mapping_table_offset_) { - DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) - || mapping_iter->second == method_offsets.mapping_table_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0) - || relative_offset == method_offsets.mapping_table_offset_) - << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&mapping_table[0], mapping_table_size)) { - ReportWriteFailure("mapping table", method_idx, dex_file, *out); - return 0; - } - size_mapping_table_ += mapping_table_size; - relative_offset += mapping_table_size; - } - DCHECK_OFFSET(); - - const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable(); - size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]); - - // Deduplicate vmap tables - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter = - vmap_table_offsets_.find(&vmap_table); - if (vmap_iter != vmap_table_offsets_.end() && - relative_offset != method_offsets.vmap_table_offset_) { - DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) - || vmap_iter->second == method_offsets.vmap_table_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0) - || relative_offset == method_offsets.vmap_table_offset_) - << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&vmap_table[0], vmap_table_size)) { - ReportWriteFailure("vmap table", method_idx, dex_file, *out); - return 0; - } - size_vmap_table_ += vmap_table_size; - relative_offset += vmap_table_size; - } - DCHECK_OFFSET(); - - const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap(); - size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); - - // Deduplicate GC maps - SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = - gc_map_offsets_.find(&gc_map); - if (gc_map_iter != gc_map_offsets_.end() && - relative_offset != method_offsets.gc_map_offset_) { - DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) - || gc_map_iter->second == method_offsets.gc_map_offset_) - << PrettyMethod(method_idx, dex_file); - } else { - DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0) - || relative_offset == method_offsets.gc_map_offset_) - << PrettyMethod(method_idx, dex_file); - if (!out->WriteFully(&gc_map[0], gc_map_size)) { - ReportWriteFailure("GC map", method_idx, dex_file, *out); - return 0; - } - size_gc_map_ += gc_map_size; - relative_offset += gc_map_size; - } - DCHECK_OFFSET(); - } + #undef VISIT return relative_offset; } @@ -993,15 +1127,14 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, } OatWriter::OatClass::OatClass(size_t offset, - std::vector<CompiledMethod*>* compiled_methods, + const std::vector<CompiledMethod*>& compiled_methods, uint32_t num_non_null_compiled_methods, - mirror::Class::Status status) { - CHECK(compiled_methods != NULL); - uint32_t num_methods = compiled_methods->size(); + mirror::Class::Status status) + : compiled_methods_(compiled_methods) { + uint32_t num_methods = compiled_methods.size(); CHECK_LE(num_non_null_compiled_methods, num_methods); offset_ = offset; - compiled_methods_ = compiled_methods; oat_method_offsets_offsets_from_oat_class_.resize(num_methods); // Since both kOatClassNoneCompiled and kOatClassAllCompiled could @@ -1033,7 +1166,7 @@ OatWriter::OatClass::OatClass(size_t offset, } for (size_t i = 0; i < num_methods; i++) { - CompiledMethod* compiled_method = (*compiled_methods_)[i]; + CompiledMethod* compiled_method = compiled_methods_[i]; if (compiled_method == NULL) { oat_method_offsets_offsets_from_oat_class_[i] = 0; } else { @@ -1048,7 +1181,6 @@ OatWriter::OatClass::OatClass(size_t offset, OatWriter::OatClass::~OatClass() { delete method_bitmap_; - delete compiled_methods_; } size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader( diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index bab1a26d44..1abacd831a 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -50,16 +50,30 @@ class OutputStream; // ... // OatClass[C] // +// GcMap one variable sized blob with GC map. +// GcMap GC maps are deduplicated. +// ... +// GcMap +// +// VmapTable one variable sized VmapTable blob (quick compiler only). +// VmapTable VmapTables are deduplicated. +// ... +// VmapTable +// +// MappingTable one variable sized blob with MappingTable (quick compiler only). +// MappingTable MappingTables are deduplicated. +// ... +// MappingTable +// // padding if necessary so that the following code will be page aligned // -// CompiledMethod one variable sized blob with the contents of each CompiledMethod -// CompiledMethod -// CompiledMethod -// CompiledMethod -// CompiledMethod -// CompiledMethod +// OatMethodHeader fixed size header for a CompiledMethod including the size of the MethodCode. +// MethodCode one variable sized blob with the code of a CompiledMethod. +// OatMethodHeader (OatMethodHeader, MethodCode) pairs are deduplicated. +// MethodCode // ... -// CompiledMethod +// OatMethodHeader +// MethodCode // class OatWriter { public: @@ -96,43 +110,47 @@ class OatWriter { } private: + // The DataAccess classes are helper classes that provide access to members related to + // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away + // we can share a lot of code for processing the maps with template classes below. + struct GcMapDataAccess; + struct MappingTableDataAccess; + struct VmapTableDataAccess; + + // The function VisitDexMethods() below iterates through all the methods in all + // the compiled dex files in order of their definitions. The method visitor + // classes provide individual bits of processing for each of the passes we need to + // first collect the data we want to write to the oat file and then, in later passes, + // to actually write it. + class DexMethodVisitor; + class OatDexMethodVisitor; + class InitOatClassesMethodVisitor; + class InitCodeMethodVisitor; + template <typename DataAccess> + class InitMapMethodVisitor; + class InitImageMethodVisitor; + class WriteCodeMethodVisitor; + template <typename DataAccess> + class WriteMapMethodVisitor; + + // Visit all the methods in all the compiled dex files in their definition order + // with a given DexMethodVisitor. + bool VisitDexMethods(DexMethodVisitor* visitor); + size_t InitOatHeader(); size_t InitOatDexFiles(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) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t InitOatCodeDexFiles(size_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t InitOatCodeDexFile(size_t offset, - size_t* oat_class_index, - const DexFile& dex_file) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t InitOatCodeClassDef(size_t offset, - size_t oat_class_index, size_t class_def_index, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index, - size_t class_def_method_index, size_t* method_offsets_index, - bool is_native, InvokeType type, uint32_t method_idx, const DexFile&) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool WriteTables(OutputStream* out, const size_t file_offset); - size_t WriteCode(OutputStream* out, const size_t file_offset); + 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); - size_t WriteCodeDexFile(OutputStream* out, const size_t file_offset, size_t relative_offset, - size_t* oat_class_index, const DexFile& dex_file); - size_t WriteCodeClassDef(OutputStream* out, const size_t file_offset, size_t relative_offset, - size_t oat_class_index, const DexFile& dex_file, - const DexFile::ClassDef& class_def); - size_t WriteCodeMethod(OutputStream* out, const size_t file_offset, size_t relative_offset, - size_t oat_class_index, size_t class_def_method_index, - size_t* method_offsets_index, bool is_static, uint32_t method_idx, - const DexFile& dex_file); - - void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file, - const OutputStream& out) const; class OatDexFile { public: @@ -159,7 +177,7 @@ class OatWriter { class OatClass { public: explicit OatClass(size_t offset, - std::vector<CompiledMethod*>* compiled_methods, + const std::vector<CompiledMethod*>& compiled_methods, uint32_t num_non_null_compiled_methods, mirror::Class::Status status); ~OatClass(); @@ -170,8 +188,8 @@ class OatWriter { bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const { - DCHECK(compiled_methods_ != NULL); - return (*compiled_methods_)[class_def_method_index]; + DCHECK_LT(class_def_method_index, compiled_methods_.size()); + return compiled_methods_[class_def_method_index]; } // Offset of start of OatClass from beginning of OatHeader. It is @@ -182,7 +200,7 @@ class OatWriter { size_t offset_; // CompiledMethods for each class_def_method_index, or NULL if no method is available. - std::vector<CompiledMethod*>* compiled_methods_; + std::vector<CompiledMethod*> compiled_methods_; // Offset from OatClass::offset_ to the OatMethodOffsets for the // class_def_method_index. If 0, it means the corresponding @@ -265,7 +283,7 @@ class OatWriter { uint32_t size_quick_resolution_trampoline_; uint32_t size_quick_to_interpreter_bridge_; uint32_t size_trampoline_alignment_; - uint32_t size_code_size_; + uint32_t size_method_header_; uint32_t size_code_; uint32_t size_code_alignment_; uint32_t size_mapping_table_; @@ -281,13 +299,6 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; - // Code mappings for deduplication. Deduplication is already done on a pointer basis by the - // compiler driver, so we can simply compare the pointers to find out if things are duplicated. - SafeMap<const std::vector<uint8_t>*, uint32_t> code_offsets_; - SafeMap<const std::vector<uint8_t>*, uint32_t> vmap_table_offsets_; - SafeMap<const std::vector<uint8_t>*, uint32_t> mapping_table_offsets_; - SafeMap<const std::vector<uint8_t>*, uint32_t> gc_map_offsets_; - DISALLOW_COPY_AND_ASSIGN(OatWriter); }; |