diff options
Diffstat (limited to 'compiler/oat_writer.cc')
| -rw-r--r-- | compiler/oat_writer.cc | 279 |
1 files changed, 242 insertions, 37 deletions
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 0ea11255a8..105db1d2d0 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -104,6 +104,13 @@ inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& // Defines the location of the raw dex file to write. class OatWriter::DexFileSource { public: + enum Type { + kNone, + kZipEntry, + kRawFile, + kRawData, + }; + explicit DexFileSource(ZipEntry* zip_entry) : type_(kZipEntry), source_(zip_entry) { DCHECK(source_ != nullptr); @@ -119,6 +126,7 @@ class OatWriter::DexFileSource { DCHECK(source_ != nullptr); } + Type GetType() const { return type_; } bool IsZipEntry() const { return type_ == kZipEntry; } bool IsRawFile() const { return type_ == kRawFile; } bool IsRawData() const { return type_ == kRawData; } @@ -147,13 +155,6 @@ class OatWriter::DexFileSource { } private: - enum Type { - kNone, - kZipEntry, - kRawFile, - kRawData, - }; - Type type_; const void* source_; }; @@ -325,6 +326,7 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings, ProfileCo size_relative_call_thunks_(0), size_misc_thunks_(0), size_vmap_table_(0), + size_method_info_(0), size_oat_dex_file_location_size_(0), size_oat_dex_file_location_data_(0), size_oat_dex_file_location_checksum_(0), @@ -808,6 +810,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size()); OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_]; uint32_t vmap_table_offset = method_header->GetVmapTableOffset(); + uint32_t method_info_offset = method_header->GetMethodInfoOffset(); // The code offset was 0 when the mapping/vmap table offset was set, so it's set // to 0-offset and we need to adjust it by code_offset. uint32_t code_offset = quick_code_offset - thumb_offset; @@ -818,13 +821,18 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { vmap_table_offset += code_offset; DCHECK_LT(vmap_table_offset, code_offset); } + if (method_info_offset != 0u) { + method_info_offset += code_offset; + DCHECK_LT(method_info_offset, code_offset); + } } else { + CHECK(compiled_method->GetMethodInfo().empty()); if (kIsVdexEnabled) { // We write the offset in the .vdex file. DCHECK_EQ(vmap_table_offset, 0u); vmap_table_offset = current_quickening_info_offset_; - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - current_quickening_info_offset_ += map.size() * sizeof(map.front()); + ArrayRef<const uint8_t> vmap_table = compiled_method->GetVmapTable(); + current_quickening_info_offset_ += vmap_table.size() * sizeof(vmap_table.front()); } else { // We write the offset of the quickening info relative to the code. vmap_table_offset += code_offset; @@ -835,6 +843,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { uint32_t core_spill_mask = compiled_method->GetCoreSpillMask(); uint32_t fp_spill_mask = compiled_method->GetFpSpillMask(); *method_header = OatQuickMethodHeader(vmap_table_offset, + method_info_offset, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -908,6 +917,9 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { if (UNLIKELY(lhs->GetVmapTable().data() != rhs->GetVmapTable().data())) { return lhs->GetVmapTable().data() < rhs->GetVmapTable().data(); } + if (UNLIKELY(lhs->GetMethodInfo().data() != rhs->GetMethodInfo().data())) { + return lhs->GetMethodInfo().data() < rhs->GetMethodInfo().data(); + } if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) { return lhs->GetPatches().data() < rhs->GetPatches().data(); } @@ -982,20 +994,103 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { SafeMap<const uint8_t*, uint32_t> dedupe_map_; }; +class OatWriter::InitMethodInfoVisitor : public OatDexMethodVisitor { + public: + InitMethodInfoVisitor(OatWriter* writer, size_t offset) : OatDexMethodVisitor(writer, offset) {} + + bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it ATTRIBUTE_UNUSED) + REQUIRES_SHARED(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(oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset(), 0u); + ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo(); + const uint32_t map_size = map.size() * sizeof(map[0]); + if (map_size != 0u) { + size_t offset = dedupe_map_.GetOrCreate( + map.data(), + [this, map_size]() { + uint32_t new_offset = offset_; + offset_ += map_size; + return new_offset; + }); + // Code offset is not initialized yet, so set the map offset to 0u-offset. + DCHECK_EQ(oat_class->method_offsets_[method_offsets_index_].code_offset_, 0u); + oat_class->method_headers_[method_offsets_index_].SetMethodInfoOffset(0u - offset); + } + ++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 uint8_t*, uint32_t> dedupe_map_; +}; + class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { public: - InitImageMethodVisitor(OatWriter* writer, size_t offset) + InitImageMethodVisitor(OatWriter* writer, + size_t offset, + const std::vector<const DexFile*>* dex_files) : OatDexMethodVisitor(writer, offset), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())) { + pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), + dex_files_(dex_files), + class_linker_(Runtime::Current()->GetClassLinker()) { + } + + // Handle copied methods here. Copy pointer to quick code from + // an origin method to a copied method only if they are + // in the same oat file. If the origin and the copied methods are + // in different oat files don't touch the copied method. + // References to other oat files are not supported yet. + bool StartClass(const DexFile* dex_file, size_t class_def_index) + REQUIRES_SHARED(Locks::mutator_lock_) { + OatDexMethodVisitor::StartClass(dex_file, class_def_index); + // Skip classes that are not in the image. + if (!IsImageClass()) { + return true; + } + ScopedObjectAccessUnchecked soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = hs.NewHandle( + class_linker_->FindDexCache(Thread::Current(), *dex_file)); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_); + if (klass != nullptr) { + for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) { + // Find origin method. Declaring class and dex_method_idx + // in the copied method should be the same as in the origin + // method. + mirror::Class* declaring_class = method.GetDeclaringClass(); + ArtMethod* origin = declaring_class->FindDeclaredVirtualMethod( + declaring_class->GetDexCache(), + method.GetDexMethodIndex(), + pointer_size_); + CHECK(origin != nullptr); + if (IsInOatFile(&declaring_class->GetDexFile())) { + const void* code_ptr = + origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); + if (code_ptr == nullptr) { + methods_to_process_.push_back(std::make_pair(&method, origin)); + } else { + method.SetEntryPointFromQuickCompiledCodePtrSize( + code_ptr, pointer_size_); + } + } + } + } + return true; } bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::TypeId& type_id = - dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); - const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); // Skip methods that are not in the image. - if (!writer_->GetCompilerDriver()->IsImageClass(class_descriptor)) { + if (!IsImageClass()) { return true; } @@ -1009,17 +1104,16 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ++method_offsets_index_; } - ClassLinker* linker = Runtime::Current()->GetClassLinker(); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache( + Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker_->FindDexCache( Thread::Current(), *dex_file_))); ArtMethod* method; if (writer_->HasBootImage()) { const InvokeType invoke_type = it.GetMethodInvokeType( dex_file_->GetClassDef(class_def_index_)); - method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>( *dex_file_, it.GetMemberIndex(), dex_cache, @@ -1039,7 +1133,8 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { // Should already have been resolved by the compiler, just peek into the dex cache. // It may not be resolved if the class failed to verify, in this case, don't set the // entrypoint. This is not fatal since the dex cache will contain a resolution method. - method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), linker->GetImagePointerSize()); + method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), + class_linker_->GetImagePointerSize()); } if (method != nullptr && compiled_method != nullptr && @@ -1051,8 +1146,38 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { return true; } + // Check whether current class is image class + bool IsImageClass() { + const DexFile::TypeId& type_id = + dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); + const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); + return writer_->GetCompilerDriver()->IsImageClass(class_descriptor); + } + + // Check whether specified dex file is in the compiled oat file. + bool IsInOatFile(const DexFile* dex_file) { + return ContainsElement(*dex_files_, dex_file); + } + + // Assign a pointer to quick code for copied methods + // not handled in the method StartClass + void Postprocess() { + for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) { + ArtMethod* method = p.first; + ArtMethod* origin = p.second; + const void* code_ptr = + origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); + if (code_ptr != nullptr) { + method->SetEntryPointFromQuickCompiledCodePtrSize(code_ptr, pointer_size_); + } + } + } + protected: const PointerSize pointer_size_; + const std::vector<const DexFile*>* dex_files_; + ClassLinker* const class_linker_; + std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_; }; class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { @@ -1224,7 +1349,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { break; } default: { - DCHECK_EQ(patch.GetType(), LinkerPatch::Type::kRecordPosition); + DCHECK(false) << "Unexpected linker patch type: " << patch.GetType(); break; } } @@ -1315,12 +1440,10 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedObjectAccessUnchecked soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); ClassLinker* linker = Runtime::Current()->GetClassLinker(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(GetDexCache(patch.TargetStringDexFile()))); mirror::String* string = linker->LookupString(*patch.TargetStringDexFile(), patch.TargetStringIndex(), - dex_cache); + GetDexCache(patch.TargetStringDexFile())); DCHECK(string != nullptr); DCHECK(writer_->HasBootImage() || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); @@ -1433,7 +1556,7 @@ class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - if (compiled_method != nullptr) { // ie. not an abstract method + if (compiled_method != nullptr) { // i.e. not an abstract method size_t file_offset = file_offset_; OutputStream* out = out_; @@ -1482,6 +1605,63 @@ class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { } }; +class OatWriter::WriteMethodInfoVisitor : public OatDexMethodVisitor { + public: + WriteMethodInfoVisitor(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 != nullptr) { // i.e. not an abstract method + size_t file_offset = file_offset_; + OutputStream* out = out_; + uint32_t map_offset = oat_class->method_headers_[method_offsets_index_].GetMethodInfoOffset(); + uint32_t code_offset = oat_class->method_offsets_[method_offsets_index_].code_offset_; + ++method_offsets_index_; + DCHECK((compiled_method->GetMethodInfo().size() == 0u && map_offset == 0u) || + (compiled_method->GetMethodInfo().size() != 0u && map_offset != 0u)) + << compiled_method->GetMethodInfo().size() << " " << map_offset << " " + << dex_file_->PrettyMethod(it.GetMemberIndex()); + if (map_offset != 0u) { + // Transform map_offset to actual oat data offset. + map_offset = (code_offset - compiled_method->CodeDelta()) - map_offset; + DCHECK_NE(map_offset, 0u); + DCHECK_LE(map_offset, offset_) << dex_file_->PrettyMethod(it.GetMemberIndex()); + + ArrayRef<const uint8_t> map = compiled_method->GetMethodInfo(); + size_t map_size = map.size() * sizeof(map[0]); + if (map_offset == offset_) { + // Write deduplicated map (code info for Optimizing or transformation info for dex2dex). + if (UNLIKELY(!out->WriteFully(map.data(), 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 map for " + << dex_file_->PrettyMethod(it.GetMemberIndex()) << " 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_) { @@ -1575,11 +1755,18 @@ size_t OatWriter::InitOatMaps(size_t offset) { if (!compiler_driver_->GetCompilerOptions().IsAnyMethodCompilationEnabled()) { return offset; } - InitMapMethodVisitor visitor(this, offset); - bool success = VisitDexMethods(&visitor); - DCHECK(success); - offset = visitor.GetOffset(); - + { + InitMapMethodVisitor visitor(this, offset); + bool success = VisitDexMethods(&visitor); + DCHECK(success); + offset = visitor.GetOffset(); + } + { + InitMethodInfoVisitor visitor(this, offset); + bool success = VisitDexMethods(&visitor); + DCHECK(success); + offset = visitor.GetOffset(); + } return offset; } @@ -1630,8 +1817,9 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { offset = code_visitor.GetOffset(); if (HasImage()) { - InitImageMethodVisitor image_visitor(this, offset); + InitImageMethodVisitor image_visitor(this, offset, dex_files_); success = VisitDexMethods(&image_visitor); + image_visitor.Postprocess(); DCHECK(success); offset = image_visitor.GetOffset(); } @@ -1919,6 +2107,7 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_relative_call_thunks_); DO_STAT(size_misc_thunks_); DO_STAT(size_vmap_table_); + DO_STAT(size_method_info_); DO_STAT(size_oat_dex_file_location_size_); DO_STAT(size_oat_dex_file_location_data_); DO_STAT(size_oat_dex_file_location_checksum_); @@ -2034,13 +2223,24 @@ bool OatWriter::WriteClasses(OutputStream* out) { } size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) { - size_t vmap_tables_offset = relative_offset; - WriteMapMethodVisitor visitor(this, out, file_offset, relative_offset); - if (UNLIKELY(!VisitDexMethods(&visitor))) { - return 0; + { + size_t vmap_tables_offset = relative_offset; + WriteMapMethodVisitor visitor(this, out, file_offset, relative_offset); + if (UNLIKELY(!VisitDexMethods(&visitor))) { + return 0; + } + relative_offset = visitor.GetOffset(); + size_vmap_table_ = relative_offset - vmap_tables_offset; + } + { + size_t method_infos_offset = relative_offset; + WriteMethodInfoVisitor visitor(this, out, file_offset, relative_offset); + if (UNLIKELY(!VisitDexMethods(&visitor))) { + return 0; + } + relative_offset = visitor.GetOffset(); + size_method_info_ = relative_offset - method_infos_offset; } - relative_offset = visitor.GetOffset(); - size_vmap_table_ = relative_offset - vmap_tables_offset; return relative_offset; } @@ -2259,6 +2459,10 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); std::unique_ptr<MemMap> mem_map( zip_entry->ExtractToMemMap(location.c_str(), "classes.dex", &error_msg)); + if (mem_map == nullptr) { + LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg; + return false; + } dex_file = DexFile::Open(location, zip_entry->GetCrc32(), std::move(mem_map), @@ -2266,7 +2470,8 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil /* verify_checksum */ true, &error_msg); } else { - DCHECK(oat_dex_file->source_.IsRawFile()); + CHECK(oat_dex_file->source_.IsRawFile()) + << static_cast<size_t>(oat_dex_file->source_.GetType()); File* raw_file = oat_dex_file->source_.GetRawFile(); dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg); } |