Merge "Separate maps from code in oat file."
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index dc66e9c..2114fe9 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 @@
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 @@
offset = InitOatClasses(offset);
}
{
+ TimingLogger::ScopedSplit split("InitOatMaps", timings);
+ offset = InitOatMaps(offset);
+ }
+ {
TimingLogger::ScopedSplit split("InitOatCode", timings);
offset = InitOatCode(offset);
}
@@ -118,6 +130,585 @@
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::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;
+ InitOatClassesMethodVisitor visitor(this, offset);
+ bool success = VisitDexMethods(&visitor);
+ CHECK(success);
+ offset = visitor.GetOffset();
- // 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();
+ // 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::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;
-}
+ #define VISIT(VisitorType) \
+ do { \
+ VisitorType visitor(this, offset); \
+ bool success = VisitDexMethods(&visitor); \
+ DCHECK(success); \
+ offset = visitor.GetOffset(); \
+ } while (false)
-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)++;
- }
-
-
+ 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 @@
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 @@
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 @@
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::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;
-}
+ #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::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;
-}
+ VISIT(WriteCodeMethodVisitor);
-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();
-}
-
-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;
-}
-
-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 @@
}
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 @@
}
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() {
delete method_bitmap_;
- delete compiled_methods_;
}
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index bab1a26..1abacd8 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -50,16 +50,30 @@
// ...
// 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 @@
}
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 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 @@
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 @@
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 @@
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 @@
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);
};
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index d5eccaf..5d62b88 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -22,6 +22,7 @@
#include "dex_file.h"
#include "entrypoints/entrypoint_utils.h"
#include "object_array.h"
+#include "oat.h"
#include "runtime.h"
namespace art {
@@ -83,7 +84,7 @@
}
// TODO: make this Thumb2 specific
code &= ~0x1;
- return reinterpret_cast<uint32_t*>(code)[-1];
+ return reinterpret_cast<OatMethodHeader*>(code)[-1].code_size_;
}
inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 38e44be..d684266 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -270,9 +270,11 @@
return pc == 0;
}
/*
- * During a stack walk, a return PC may point to the end of the code + 1
- * (in the case that the last instruction is a call that isn't expected to
+ * During a stack walk, a return PC may point past-the-end of the code
+ * in the case that the last instruction is a call that isn't expected to
* return. Thus, we check <= code + GetCodeSize().
+ *
+ * NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state.
*/
return (code <= pc && pc <= code + GetCodeSize());
}
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 246e090..d01dc72 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '2', '0', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '2', '1', '\0' };
OatHeader::OatHeader() {
memset(this, 0, sizeof(*this));
@@ -372,4 +372,14 @@
OatMethodOffsets::~OatMethodOffsets() {}
+OatMethodHeader::OatMethodHeader()
+ : code_size_(0)
+{}
+
+OatMethodHeader::OatMethodHeader(uint32_t code_size)
+ : code_size_(code_size)
+{}
+
+OatMethodHeader::~OatMethodHeader() {}
+
} // namespace art
diff --git a/runtime/oat.h b/runtime/oat.h
index 2851f5c..035aba1 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -155,6 +155,19 @@
uint32_t gc_map_offset_;
};
+// OatMethodHeader precedes the raw code chunk generated by the Quick compiler.
+class PACKED(4) OatMethodHeader {
+ public:
+ OatMethodHeader();
+
+ explicit OatMethodHeader(uint32_t code_size);
+
+ ~OatMethodHeader();
+
+ // The code size in bytes.
+ uint32_t code_size_;
+};
+
} // namespace art
#endif // ART_RUNTIME_OAT_H_