diff options
84 files changed, 1302 insertions, 724 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index f5a95fa0cf..08962526dd 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -49,6 +49,11 @@ endif # Enable the read barrier by default. ART_USE_READ_BARRIER ?= true +# Default compact dex level to none. +ifeq ($(ART_DEFAULT_COMPACT_DEX_LEVEL),) +ART_DEFAULT_COMPACT_DEX_LEVEL := none +endif + ART_CPP_EXTENSION := .cc ifndef LIBART_IMG_HOST_BASE_ADDRESS diff --git a/build/art.go b/build/art.go index 5704b43834..3f598da00a 100644 --- a/build/art.go +++ b/build/art.go @@ -66,6 +66,9 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { "-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1") } + cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "none") + cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel) + // We need larger stack overflow guards for ASAN, as the compiled code will have // larger frame sizes. For simplicity, just use global not-target-specific cflags. // Note: We increase this for both debug and non-debug, as the overflow gap will diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index d0c98a7b79..27b70c8caa 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -207,13 +207,12 @@ void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder, } // Write .eh_frame/.debug_frame section. - auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT - ? builder->GetDebugFrame() - : builder->GetEhFrame()); + const bool is_debug_frame = format == dwarf::DW_DEBUG_FRAME_FORMAT; + auto* cfi_section = (is_debug_frame ? builder->GetDebugFrame() : builder->GetEhFrame()); { cfi_section->Start(); const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); - const Elf_Addr cfi_address = cfi_section->GetAddress(); + const Elf_Addr cfi_address = (is_debug_frame ? 0 : cfi_section->GetAddress()); const Elf_Addr cie_address = cfi_address; Elf_Addr buffer_address = cfi_address; std::vector<uint8_t> buffer; // Small temporary buffer. diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index d5999941d7..107ed488cd 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -22,6 +22,7 @@ #include <vector> #include "art_field-inl.h" +#include "code_item_accessors-inl.h" #include "debug/dwarf/debug_abbrev_writer.h" #include "debug/dwarf/debug_info_entry_writer.h" #include "debug/elf_compilation_unit.h" @@ -48,10 +49,10 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) { std::vector<const char*> names; - if (mi->code_item != nullptr) { + CodeItemDebugInfoAccessor accessor(mi->dex_file, mi->code_item); + if (accessor.HasCodeItem()) { DCHECK(mi->dex_file != nullptr); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*mi->dex_file, mi->code_item); - const uint8_t* stream = mi->dex_file->GetDebugInfoStream(debug_info_offset); + const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset()); if (stream != nullptr) { DecodeUnsignedLeb128(&stream); // line. uint32_t parameters_size = DecodeUnsignedLeb128(&stream); @@ -162,7 +163,7 @@ class ElfCompilationUnitWriter { for (auto mi : compilation_unit.methods) { DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - const DexFile::CodeItem* dex_code = mi->code_item; + CodeItemDebugInfoAccessor accessor(dex, mi->code_item); const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index); const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method); const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto); @@ -204,13 +205,13 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. std::vector<DexRegisterMap> dex_reg_maps; - if (dex_code != nullptr && mi->code_info != nullptr) { + if (accessor.HasCodeItem() && mi->code_info != nullptr) { const CodeInfo code_info(mi->code_info); CodeInfoEncoding encoding = code_info.ExtractEncoding(); for (size_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); ++s) { const StackMap& stack_map = code_info.GetStackMapAt(s, encoding); dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( - stack_map, encoding, dex_code->registers_size_)); + stack_map, encoding, accessor.RegistersSize())); } } @@ -224,9 +225,9 @@ class ElfCompilationUnitWriter { WriteName("this"); info_.WriteFlagPresent(DW_AT_artificial); WriteLazyType(dex_class_desc); - if (dex_code != nullptr) { + if (accessor.HasCodeItem()) { // Write the stack location of the parameter. - const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; + const uint32_t vreg = accessor.RegistersSize() - accessor.InsSize() + arg_reg; const bool is64bitValue = false; WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } @@ -244,30 +245,31 @@ class ElfCompilationUnitWriter { const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_); WriteLazyType(type_desc); const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J'; - if (dex_code != nullptr) { + if (accessor.HasCodeItem()) { // Write the stack location of the parameter. - const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; + const uint32_t vreg = accessor.RegistersSize() - accessor.InsSize() + arg_reg; WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } arg_reg += is64bitValue ? 2 : 1; info_.EndTag(); } - if (dex_code != nullptr) { - DCHECK_EQ(arg_reg, dex_code->ins_size_); + if (accessor.HasCodeItem()) { + DCHECK_EQ(arg_reg, accessor.InsSize()); } } // Write local variables. LocalInfos local_infos; - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, dex_code); - if (dex->DecodeDebugLocalInfo(dex_code, - debug_info_offset, + if (dex->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), is_static, mi->dex_method_index, LocalInfoCallback, &local_infos)) { for (const DexFile::LocalInfo& var : local_infos) { - if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) { + if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) { info_.StartTag(DW_TAG_variable); WriteName(var.name_); WriteLazyType(var.descriptor_); @@ -296,7 +298,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector<uint8_t> buffer; buffer.reserve(info_.data()->size() + KB); - const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition(); // All compilation units share single table which is at the start of .debug_abbrev. const size_t debug_abbrev_offset = 0; WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); @@ -461,7 +463,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector<uint8_t> buffer; buffer.reserve(info_.data()->size() + KB); - const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition(); // All compilation units share single table which is at the start of .debug_abbrev. const size_t debug_abbrev_offset = 0; WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 943e03a765..d7fd52448c 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -60,7 +60,7 @@ class ElfDebugLineWriter { ? builder_->GetText()->GetAddress() : 0; - compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetSize(); + compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetPosition(); std::vector<dwarf::FileEntry> files; std::unordered_map<std::string, size_t> files_map; @@ -159,9 +159,9 @@ class ElfDebugLineWriter { PositionInfos dex2line_map; DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, mi->code_item); - if (!dex->DecodeDebugPositionInfo( - mi->code_item, debug_info_offset, PositionInfoCallback, &dex2line_map)) { + CodeItemDebugInfoAccessor accessor(dex, mi->code_item); + const uint32_t debug_info_offset = accessor.DebugInfoOffset(); + if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) { continue; } @@ -268,7 +268,7 @@ class ElfDebugLineWriter { } std::vector<uint8_t> buffer; buffer.reserve(opcodes.data()->size() + KB); - size_t offset = builder_->GetDebugLine()->GetSize(); + size_t offset = builder_->GetDebugLine()->GetPosition(); WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_); builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size()); return buffer.size(); diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index bb856b29f4..1d609af4e6 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -251,7 +251,10 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). // kInRegisterHigh and kInFpuRegisterHigh should be handled by // the special cases above and they should not occur alone. - LOG(ERROR) << "Unexpected register location kind: " << kind; + LOG(WARNING) << "Unexpected register location: " << kind + << " (This can indicate either a bug in the dexer when generating" + << " local variable information, or a bug in ART compiler." + << " Please file a bug at go/art-bug)"; break; } if (is64bitValue) { diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 33c46d7e1f..a6267292bf 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -108,29 +108,32 @@ void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder, std::vector<uint8_t> MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_size, + uint64_t text_address, size_t text_size, const ArrayRef<const MethodDebugInfo>& method_infos) { if (Is64BitInstructionSet(isa)) { return MakeMiniDebugInfoInternal<ElfTypes64>(isa, features, - rodata_size, + text_address, text_size, method_infos); } else { return MakeMiniDebugInfoInternal<ElfTypes32>(isa, features, - rodata_size, + text_address, text_size, method_infos); } } template <typename ElfTypes> -static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal( +static std::vector<uint8_t> MakeElfFileForJITInternal( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef<const MethodDebugInfo>& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& mi) { + CHECK_EQ(mi.is_code_address_text_relative, false); + ArrayRef<const MethodDebugInfo> method_infos(&mi, 1); std::vector<uint8_t> buffer; buffer.reserve(KB); linker::VectorOutputStream out("Debug ELF file", &buffer); @@ -138,23 +141,34 @@ static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal( new linker::ElfBuilder<ElfTypes>(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); - WriteDebugInfo(builder.get(), - method_infos, - dwarf::DW_DEBUG_FRAME_FORMAT, - false /* write_oat_patches */); + if (mini_debug_info) { + std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa, + features, + mi.code_address, + mi.code_size, + method_infos); + builder->WriteSection(".gnu_debugdata", &mdi); + } else { + builder->GetText()->AllocateVirtualMemory(mi.code_address, mi.code_size); + WriteDebugInfo(builder.get(), + method_infos, + dwarf::DW_DEBUG_FRAME_FORMAT, + false /* write_oat_patches */); + } builder->End(); CHECK(builder->Good()); return buffer; } -std::vector<uint8_t> WriteDebugElfFileForMethods( +std::vector<uint8_t> MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef<const MethodDebugInfo>& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& method_info) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodsInternal<ElfTypes64>(isa, features, method_infos); + return MakeElfFileForJITInternal<ElfTypes64>(isa, features, mini_debug_info, method_info); } else { - return WriteDebugElfFileForMethodsInternal<ElfTypes32>(isa, features, method_infos); + return MakeElfFileForJITInternal<ElfTypes32>(isa, features, mini_debug_info, method_info); } } diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index d24ca9b203..a47bf076b9 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -43,14 +43,15 @@ void WriteDebugInfo( std::vector<uint8_t> MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + uint64_t text_section_address, size_t text_section_size, const ArrayRef<const MethodDebugInfo>& method_infos); -std::vector<uint8_t> WriteDebugElfFileForMethods( +std::vector<uint8_t> MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef<const MethodDebugInfo>& method_infos); + bool mini_debug_info, + const MethodDebugInfo& method_info); std::vector<uint8_t> WriteDebugElfFileForClasses( InstructionSet isa, diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 1cdf6b0ad1..78b8e2780c 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -80,7 +80,7 @@ template <typename ElfTypes> static std::vector<uint8_t> MakeMiniDebugInfoInternal( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + typename ElfTypes::Addr text_section_address, size_t text_section_size, const ArrayRef<const MethodDebugInfo>& method_infos) { std::vector<uint8_t> buffer; @@ -88,11 +88,9 @@ static std::vector<uint8_t> MakeMiniDebugInfoInternal( linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer); std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( new linker::ElfBuilder<ElfTypes>(isa, features, &out)); - builder->Start(); - // Mirror .rodata and .text as NOBITS sections. - // It is needed to detected relocations after compression. - builder->GetRoData()->WriteNoBitsSection(rodata_section_size); - builder->GetText()->WriteNoBitsSection(text_section_size); + builder->Start(false /* write_program_headers */); + // Mirror .text as NOBITS section since the added symbols will reference it. + builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size); WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); WriteCFISection(builder.get(), method_infos, diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 0907e102a0..57e010f232 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -79,8 +79,9 @@ static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder, last_name_offset = name_offset; } - const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr; - uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0); + const auto* text = builder->GetText(); + uint64_t address = info.code_address; + address += info.is_code_address_text_relative ? text->GetAddress() : 0; // Add in code delta, e.g., thumb bit 0 for Thumb2 code. address += CompiledMethod::CodeDelta(info.isa); symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC); diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 476903522e..ead909af9a 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -296,7 +296,6 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, ClassLinker* class_linker = unit_.GetClassLinker(); ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - GetDexFile(), method_idx, unit_.GetDexCache(), unit_.GetClassLoader(), diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 42fc4aa5ba..294072d7e7 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -33,13 +33,15 @@ namespace art { inline ObjPtr<mirror::Class> CompilerDriver::ResolveClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index, + const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + dex::TypeIndex cls_index, const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - ObjPtr<mirror::Class> cls = mUnit->GetClassLinker()->ResolveType( - *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); + ObjPtr<mirror::Class> cls = + mUnit->GetClassLinker()->ResolveType(cls_index, dex_cache, class_loader); DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); if (UNLIKELY(cls == nullptr)) { // Clean up any exception left by type resolution. @@ -49,8 +51,10 @@ inline ObjPtr<mirror::Class> CompilerDriver::ResolveClass( } inline ObjPtr<mirror::Class> CompilerDriver::ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) { + const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + const DexCompilationUnit* mUnit) { DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); const DexFile::MethodId& referrer_method_id = @@ -105,7 +109,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type); + method_idx, dex_cache, class_loader, /* referrer */ nullptr, invoke_type); if (UNLIKELY(resolved_method == nullptr)) { DCHECK(soa.Self()->IsExceptionPending()); // Clean up any exception left by type resolution. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fcc6b8be9e..0631c0f12c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1048,22 +1048,21 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { for (const auto& exception_type : unresolved_exception_types) { dex::TypeIndex exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; - StackHandleScope<2> hs2(self); + StackHandleScope<1> hs2(self); Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); - Handle<mirror::Class> klass(hs2.NewHandle( + ObjPtr<mirror::Class> klass = (dex_cache != nullptr) - ? class_linker->ResolveType(*dex_file, - exception_type_idx, + ? class_linker->ResolveType(exception_type_idx, dex_cache, ScopedNullHandle<mirror::ClassLoader>()) - : nullptr)); + : nullptr; if (klass == nullptr) { const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); LOG(FATAL) << "Failed to resolve class " << descriptor; } - DCHECK(java_lang_Throwable->IsAssignableFrom(klass.Get())); + DCHECK(java_lang_Throwable->IsAssignableFrom(klass)); } // Resolving exceptions may load classes that reference more exceptions, iterate until no // more are found @@ -1638,7 +1637,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { soa.Self(), dex_file))); // Resolve the class. ObjPtr<mirror::Class> klass = - class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, class_loader); + class_linker->ResolveType(class_def.class_idx_, dex_cache, class_loader); bool resolve_fields_and_methods; if (klass == nullptr) { // Class couldn't be resolved, for example, super-class is in a different dex file. Don't @@ -1690,7 +1689,10 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { if (resolve_fields_and_methods) { while (it.HasNextMethod()) { ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, + it.GetMemberIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, it.GetMethodInvokeType(class_def)); if (method == nullptr) { CheckAndClearResolveException(soa.Self()); @@ -1726,7 +1728,7 @@ class ResolveTypeVisitor : public CompilationVisitor { dex_file, class_loader.Get()))); ObjPtr<mirror::Class> klass = (dex_cache != nullptr) - ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader) + ? class_linker->ResolveType(dex::TypeIndex(type_idx), dex_cache, class_loader) : nullptr; if (klass == nullptr) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index ab788e326f..e001726c95 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -219,15 +219,17 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Resolve compiling method's class. Returns null on failure. - ObjPtr<mirror::Class> ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) + ObjPtr<mirror::Class> ResolveCompilingMethodsClass(const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<mirror::Class> ResolveClass( - const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, dex::TypeIndex type_index, - const DexCompilationUnit* mUnit) + ObjPtr<mirror::Class> ResolveClass(const ScopedObjectAccess& soa, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, + dex::TypeIndex type_index, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a field. Returns null on failure, including incompatible class change. @@ -240,10 +242,10 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset. - std::pair<bool, bool> IsFastInstanceField( - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::Class> referrer_class, - ArtField* resolved_field, uint16_t field_idx) + std::pair<bool, bool> IsFastInstanceField(ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::Class> referrer_class, + ArtField* resolved_field, + uint16_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a method. Returns null on failure, including incompatible class change. diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index b30b55e9b4..aa3cd98595 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -108,8 +108,6 @@ class ElfBuilder FINAL { section_index_(0), name_(name), link_(link), - started_(false), - finished_(false), phdr_flags_(PF_R), phdr_type_(0) { DCHECK_GE(align, 1u); @@ -120,90 +118,62 @@ class ElfBuilder FINAL { header_.sh_entsize = entsize; } - // Start writing of this section. - void Start() { - CHECK(!started_); - CHECK(!finished_); - started_ = true; - auto& sections = owner_->sections_; - // Check that the previous section is complete. - CHECK(sections.empty() || sections.back()->finished_); - // The first ELF section index is 1. Index 0 is reserved for NULL. - section_index_ = sections.size() + 1; - // Page-align if we switch between allocated and non-allocated sections, - // or if we change the type of allocation (e.g. executable vs non-executable). - if (!sections.empty()) { - if (header_.sh_flags != sections.back()->header_.sh_flags) { - header_.sh_addralign = kPageSize; - } - } - // Align file position. - if (header_.sh_type != SHT_NOBITS) { - header_.sh_offset = owner_->AlignFileOffset(header_.sh_addralign); - } else { - header_.sh_offset = 0; - } - // Align virtual memory address. - if ((header_.sh_flags & SHF_ALLOC) != 0) { - header_.sh_addr = owner_->AlignVirtualAddress(header_.sh_addralign); - } else { - header_.sh_addr = 0; - } - // Push this section on the list of written sections. - sections.push_back(this); + // Allocate chunk of virtual memory for this section from the owning ElfBuilder. + // This must be done at the start for all SHF_ALLOC sections (i.e. mmaped by linker). + // It is fine to allocate section but never call Start/End() (e.g. the .bss section). + void AllocateVirtualMemory(Elf_Word size) { + AllocateVirtualMemory(owner_->virtual_address_, size); } - // Finish writing of this section. - void End() { - CHECK(started_); - CHECK(!finished_); - finished_ = true; - if (header_.sh_type == SHT_NOBITS) { - CHECK_GT(header_.sh_size, 0u); - } else { - // Use the current file position to determine section size. - off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent); - CHECK_GE(file_offset, (off_t)header_.sh_offset); - header_.sh_size = file_offset - header_.sh_offset; - } - if ((header_.sh_flags & SHF_ALLOC) != 0) { - owner_->virtual_address_ += header_.sh_size; - } + void AllocateVirtualMemory(Elf_Addr addr, Elf_Word size) { + CHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); + Elf_Word align = AddSection(); + CHECK_EQ(header_.sh_addr, 0u); + header_.sh_addr = RoundUp(addr, align); + CHECK(header_.sh_size == 0u || header_.sh_size == size); + header_.sh_size = size; + CHECK_LE(owner_->virtual_address_, header_.sh_addr); + owner_->virtual_address_ = header_.sh_addr + header_.sh_size; } - // Get the location of this section in virtual memory. - Elf_Addr GetAddress() const { - CHECK(started_); - return header_.sh_addr; + // Start writing file data of this section. + void Start() { + CHECK(owner_->current_section_ == nullptr); + Elf_Word align = AddSection(); + CHECK_EQ(header_.sh_offset, 0u); + header_.sh_offset = owner_->AlignFileOffset(align); + owner_->current_section_ = this; } - // Returns the size of the content of this section. - Elf_Word GetSize() const { - if (finished_) { - return header_.sh_size; - } else { - CHECK(started_); - CHECK_NE(header_.sh_type, (Elf_Word)SHT_NOBITS); - return owner_->stream_.Seek(0, kSeekCurrent) - header_.sh_offset; - } + // Finish writing file data of this section. + void End() { + CHECK(owner_->current_section_ == this); + Elf_Word position = GetPosition(); + CHECK(header_.sh_size == 0u || header_.sh_size == position); + header_.sh_size = position; + owner_->current_section_ = nullptr; + } + + // Get the number of bytes written so far. + // Only valid while writing the section. + Elf_Word GetPosition() const { + CHECK(owner_->current_section_ == this); + off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent); + DCHECK_GE(file_offset, (off_t)header_.sh_offset); + return file_offset - header_.sh_offset; } - // Write this section as "NOBITS" section. (used for the .bss section) - // This means that the ELF file does not contain the initial data for this section - // and it will be zero-initialized when the ELF file is loaded in the running program. - void WriteNoBitsSection(Elf_Word size) { + // Get the location of this section in virtual memory. + Elf_Addr GetAddress() const { DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); - header_.sh_type = SHT_NOBITS; - Start(); - header_.sh_size = size; - End(); + DCHECK_NE(header_.sh_addr, 0u); + return header_.sh_addr; } // This function always succeeds to simplify code. // Use builder's Good() to check the actual status. bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE { - CHECK(started_); - CHECK(!finished_); + CHECK(owner_->current_section_ == this); return owner_->stream_.WriteFully(buffer, byte_count); } @@ -221,19 +191,32 @@ class ElfBuilder FINAL { } Elf_Word GetSectionIndex() const { - DCHECK(started_); DCHECK_NE(section_index_, 0u); return section_index_; } private: + // Add this section to the list of generated ELF sections (if not there already). + // It also ensures the alignment is sufficient to generate valid program headers, + // since that depends on the previous section. It returns the required alignment. + Elf_Word AddSection() { + if (section_index_ == 0) { + std::vector<Section*>& sections = owner_->sections_; + Elf_Word last = sections.empty() ? PF_R : sections.back()->phdr_flags_; + if (phdr_flags_ != last) { + header_.sh_addralign = kPageSize; // Page-align if R/W/X flags changed. + } + sections.push_back(this); + section_index_ = sections.size(); // First ELF section has index 1. + } + return owner_->write_program_headers_ ? header_.sh_addralign : 1; + } + ElfBuilder<ElfTypes>* owner_; Elf_Shdr header_; Elf_Word section_index_; const std::string name_; const Section* const link_; - bool started_; - bool finished_; Elf_Word phdr_flags_; Elf_Word phdr_type_; @@ -370,7 +353,7 @@ class ElfBuilder FINAL { Elf_Word section_index; if (section != nullptr) { DCHECK_LE(section->GetAddress(), addr); - DCHECK_LE(addr, section->GetAddress() + section->GetSize()); + DCHECK_LE(addr, section->GetAddress() + section->header_.sh_size); section_index = section->GetSectionIndex(); } else { section_index = static_cast<Elf_Word>(SHN_ABS); @@ -479,6 +462,10 @@ class ElfBuilder FINAL { digest_start_(-1) { } + Elf_Word GetSize() { + return 16 + kBuildIdLen; + } + void Write() { // The size fields are 32-bit on both 32-bit and 64-bit systems, confirmed // with the 64-bit linker and libbfd code. The size of name and desc must @@ -490,6 +477,7 @@ class ElfBuilder FINAL { digest_start_ = this->Seek(0, kSeekCurrent); static_assert(kBuildIdLen % 4 == 0, "expecting a mutliple of 4 for build ID length"); this->WriteFully(std::string(kBuildIdLen, '\0').c_str(), kBuildIdLen); // desc. + DCHECK_EQ(this->GetPosition(), GetSize()); } off_t GetDigestStart() { @@ -530,6 +518,7 @@ class ElfBuilder FINAL { abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0, isa, features), build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0), + current_section_(nullptr), started_(false), write_program_headers_(false), loaded_size_(0u), @@ -545,6 +534,7 @@ class ElfBuilder FINAL { ~ElfBuilder() {} InstructionSet GetIsa() { return isa_; } + BuildIdSection* GetBuildId() { return &build_id_; } Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } Section* GetBss() { return &bss_; } @@ -622,6 +612,9 @@ class ElfBuilder FINAL { if (section->link_ != nullptr) { section->header_.sh_link = section->link_->GetSectionIndex(); } + if (section->header_.sh_offset == 0) { + section->header_.sh_type = SHT_NOBITS; + } } shstrtab_.End(); @@ -680,65 +673,57 @@ class ElfBuilder FINAL { soname = soname.substr(directory_separator_pos + 1); } - // Calculate addresses of .text, .bss and .dynstr. - DCHECK_EQ(rodata_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - DCHECK_EQ(text_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - DCHECK_EQ(bss_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - Elf_Word rodata_address = rodata_.GetAddress(); - Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize); - Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize); - Elf_Word abiflags_address = RoundUp(bss_address + bss_size, kPageSize); - Elf_Word abiflags_size = 0; + // Allocate all pre-dynamic sections. + rodata_.AllocateVirtualMemory(rodata_size); + text_.AllocateVirtualMemory(text_size); + if (bss_size != 0) { + bss_.AllocateVirtualMemory(bss_size); + } if (isa_ == InstructionSet::kMips || isa_ == InstructionSet::kMips64) { - abiflags_size = abiflags_.GetSize(); + abiflags_.AllocateVirtualMemory(abiflags_.GetSize()); } - Elf_Word dynstr_address = RoundUp(abiflags_address + abiflags_size, kPageSize); // Cache .dynstr, .dynsym and .hash data. dynstr_.Add(""); // dynstr should start with empty string. - Elf_Word rodata_index = rodata_.GetSectionIndex(); Elf_Word oatdata = dynstr_.Add("oatdata"); - dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT); + dynsym_.Add(oatdata, &rodata_, rodata_.GetAddress(), rodata_size, STB_GLOBAL, STT_OBJECT); if (text_size != 0u) { - Elf_Word text_index = rodata_index + 1u; Elf_Word oatexec = dynstr_.Add("oatexec"); - dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT); + dynsym_.Add(oatexec, &text_, text_.GetAddress(), text_size, STB_GLOBAL, STT_OBJECT); Elf_Word oatlastword = dynstr_.Add("oatlastword"); - Elf_Word oatlastword_address = text_address + text_size - 4; - dynsym_.Add(oatlastword, text_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword_address = text_.GetAddress() + text_size - 4; + dynsym_.Add(oatlastword, &text_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } else if (rodata_size != 0) { // rodata_ can be size 0 for dwarf_test. Elf_Word oatlastword = dynstr_.Add("oatlastword"); - Elf_Word oatlastword_address = rodata_address + rodata_size - 4; - dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; + dynsym_.Add(oatlastword, &rodata_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } DCHECK_LE(bss_roots_offset, bss_size); if (bss_size != 0u) { - Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u); Elf_Word oatbss = dynstr_.Add("oatbss"); - dynsym_.Add(oatbss, bss_index, bss_address, bss_roots_offset, STB_GLOBAL, STT_OBJECT); + dynsym_.Add(oatbss, &bss_, bss_.GetAddress(), bss_roots_offset, STB_GLOBAL, STT_OBJECT); DCHECK_LE(bss_methods_offset, bss_roots_offset); DCHECK_LE(bss_roots_offset, bss_size); // Add a symbol marking the start of the methods part of the .bss, if not empty. if (bss_methods_offset != bss_roots_offset) { - Elf_Word bss_methods_address = bss_address + bss_methods_offset; + Elf_Word bss_methods_address = bss_.GetAddress() + bss_methods_offset; Elf_Word bss_methods_size = bss_roots_offset - bss_methods_offset; Elf_Word oatbssroots = dynstr_.Add("oatbssmethods"); dynsym_.Add( - oatbssroots, bss_index, bss_methods_address, bss_methods_size, STB_GLOBAL, STT_OBJECT); + oatbssroots, &bss_, bss_methods_address, bss_methods_size, STB_GLOBAL, STT_OBJECT); } // Add a symbol marking the start of the GC roots part of the .bss, if not empty. if (bss_roots_offset != bss_size) { - Elf_Word bss_roots_address = bss_address + bss_roots_offset; + Elf_Word bss_roots_address = bss_.GetAddress() + bss_roots_offset; Elf_Word bss_roots_size = bss_size - bss_roots_offset; Elf_Word oatbssroots = dynstr_.Add("oatbssroots"); dynsym_.Add( - oatbssroots, bss_index, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT); + oatbssroots, &bss_, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT); } Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword"); - Elf_Word bsslastword_address = bss_address + bss_size - 4; - dynsym_.Add(oatbsslastword, bss_index, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word bsslastword_address = bss_.GetAddress() + bss_size - 4; + dynsym_.Add(oatbsslastword, &bss_, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } Elf_Word soname_offset = dynstr_.Add(soname); @@ -759,28 +744,24 @@ class ElfBuilder FINAL { hash.push_back(0); // Last symbol terminates the chain. hash_.Add(hash.data(), hash.size() * sizeof(hash[0])); - // Calculate addresses of .dynsym, .hash and .dynamic. - DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags); - DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags); - Elf_Word dynsym_address = - RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign); - Elf_Word hash_address = - RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign); - DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); - Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize); + // Allocate all remaining sections. + dynstr_.AllocateVirtualMemory(dynstr_.GetCacheSize()); + dynsym_.AllocateVirtualMemory(dynsym_.GetCacheSize()); + hash_.AllocateVirtualMemory(hash_.GetCacheSize()); Elf_Dyn dyns[] = { - { DT_HASH, { hash_address } }, - { DT_STRTAB, { dynstr_address } }, - { DT_SYMTAB, { dynsym_address } }, + { DT_HASH, { hash_.GetAddress() } }, + { DT_STRTAB, { dynstr_.GetAddress() } }, + { DT_SYMTAB, { dynsym_.GetAddress() } }, { DT_SYMENT, { sizeof(Elf_Sym) } }, { DT_STRSZ, { dynstr_.GetCacheSize() } }, { DT_SONAME, { soname_offset } }, { DT_NULL, { 0 } }, }; dynamic_.Add(&dyns, sizeof(dyns)); + dynamic_.AllocateVirtualMemory(dynamic_.GetCacheSize()); - loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize); + loaded_size_ = RoundUp(virtual_address_, kPageSize); } void WriteDynamicSection() { @@ -788,8 +769,6 @@ class ElfBuilder FINAL { dynsym_.WriteCachedSection(); hash_.WriteCachedSection(); dynamic_.WriteCachedSection(); - - CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize)); } Elf_Word GetLoadedSize() { @@ -828,10 +807,6 @@ class ElfBuilder FINAL { return stream_.Seek(RoundUp(stream_.Seek(0, kSeekCurrent), alignment), kSeekSet); } - Elf_Addr AlignVirtualAddress(size_t alignment) { - return virtual_address_ = RoundUp(virtual_address_, alignment); - } - private: static Elf_Ehdr MakeElfHeader(InstructionSet isa, const InstructionSetFeatures* features) { Elf_Ehdr elf_header = Elf_Ehdr(); @@ -902,7 +877,6 @@ class ElfBuilder FINAL { elf_header.e_ehsize = sizeof(Elf_Ehdr); elf_header.e_phentsize = sizeof(Elf_Phdr); elf_header.e_shentsize = sizeof(Elf_Shdr); - elf_header.e_phoff = sizeof(Elf_Ehdr); return elf_header; } @@ -933,6 +907,7 @@ class ElfBuilder FINAL { for (auto* section : sections_) { const Elf_Shdr& shdr = section->header_; if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { + DCHECK(shdr.sh_addr != 0u) << "Allocate virtual memory for the section"; // PT_LOAD tells the linker to mmap part of the file. // The linker can only mmap page-aligned sections. // Single PT_LOAD may contain several ELF sections. @@ -1010,6 +985,7 @@ class ElfBuilder FINAL { // List of used section in the order in which they were written. std::vector<Section*> sections_; + Section* current_section_; // The section which is currently being written. bool started_; bool write_program_headers_; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index a175c21760..8750910fe1 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -294,7 +294,7 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, // as there may be different class loaders. So only return the index if it's // the right class already resolved with the class loader. if (index.IsValid()) { - ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> resolved = compilation_unit.GetClassLinker()->LookupResolvedType( index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); if (resolved != cls) { index = dex::TypeIndex::Invalid(); @@ -682,7 +682,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( << "is invalid in location" << dex_cache->GetDexFile()->GetLocation(); return kInlineCacheNoData; } - ObjPtr<mirror::Class> clazz = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> clazz = caller_compilation_unit_.GetClassLinker()->LookupResolvedType( class_ref.type_index, dex_cache, caller_compilation_unit_.GetClassLoader().Get()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index bce4de32d5..e36d91fb05 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -442,17 +442,15 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - const uint32_t num_instructions = code_item_->insns_size_in_code_units_; + CodeItemDebugInfoAccessor accessor(dex_file_, code_item_); ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, - num_instructions, + accessor.InsnsSizeInCodeUnits(), /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_); - dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), Callback::Position, locations); // Instruction-specific tweaks. - IterationRange<DexInstructionIterator> instructions = code_item_->Instructions(); - for (const DexInstructionPcPair& inst : instructions) { + for (const DexInstructionPcPair& inst : accessor) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { // Stop in native debugger after the exception has been moved. @@ -461,7 +459,7 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { locations->ClearBit(inst.DexPc()); DexInstructionIterator next = std::next(DexInstructionIterator(inst)); DCHECK(next.DexPc() != inst.DexPc()); - if (next != instructions.end()) { + if (next != accessor.end()) { locations->SetBit(next.DexPc()); } break; @@ -796,7 +794,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - *dex_compilation_unit_->GetDexFile(), method_idx, dex_compilation_unit_->GetDexCache(), class_loader, @@ -831,7 +828,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in return nullptr; } ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType( - *dex_compilation_unit_->GetDexFile(), dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_, dex_compilation_unit_->GetDexCache().Get(), class_loader.Get()); @@ -1425,7 +1421,7 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio } static ObjPtr<mirror::Class> GetClassFrom(CompilerDriver* driver, - const DexCompilationUnit& compilation_unit) { + const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader(); Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); @@ -2934,7 +2930,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType( dex::TypeIndex type_index, const DexCompilationUnit& compilation_unit) const { - return ClassLinker::LookupResolvedType( + return compilation_unit.GetClassLinker()->LookupResolvedType( type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index a42a85dc1d..53e449bbbe 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -994,6 +994,27 @@ void InstructionSimplifierVisitor::VisitIf(HIf* instruction) { instruction->GetBlock()->SwapSuccessors(); RecordSimplification(); } + HInstruction* input = instruction->InputAt(0); + + // If a condition 'cond' is evaluated in an HIf instruction then in the successors of the + // IF_BLOCK we statically know the value of the condition (TRUE in TRUE_SUCC, FALSE in + // FALSE_SUCC). Using that we can replace another evaluation (use) EVAL of the same 'cond' + // with TRUE value (FALSE value) if every path from the ENTRY_BLOCK to EVAL_BLOCK contains the + // edge HIF_BLOCK->TRUE_SUCC (HIF_BLOCK->FALSE_SUCC). + if (!input->IsConstant()) { + HBasicBlock* true_succ = instruction->IfTrueSuccessor(); + HBasicBlock* false_succ = instruction->IfFalseSuccessor(); + + DCHECK_EQ(true_succ->GetPredecessors().size(), 1u); + input->ReplaceUsesDominatedBy( + true_succ->GetFirstInstruction(), GetGraph()->GetIntConstant(1), /* strictly */ false); + RecordSimplification(); + + DCHECK_EQ(false_succ->GetPredecessors().size(), 1u); + input->ReplaceUsesDominatedBy( + false_succ->GetFirstInstruction(), GetGraph()->GetIntConstant(0), /* strictly */ false); + RecordSimplification(); + } } void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) { diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 5f33ed6303..d39c2aded5 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1111,10 +1111,10 @@ bool HInstructionList::FoundBefore(const HInstruction* instruction1, return true; } -bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { +bool HInstruction::Dominates(HInstruction* other_instruction, bool strictly) const { if (other_instruction == this) { // An instruction does not strictly dominate itself. - return false; + return !strictly; } HBasicBlock* block = GetBlock(); HBasicBlock* other_block = other_instruction->GetBlock(); @@ -1148,6 +1148,10 @@ bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { } } +bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const { + return Dominates(other_instruction, /* strictly */ true); +} + void HInstruction::RemoveEnvironment() { RemoveEnvironmentUses(this); environment_ = nullptr; @@ -1170,14 +1174,16 @@ void HInstruction::ReplaceWith(HInstruction* other) { DCHECK(env_uses_.empty()); } -void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) { +void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, + HInstruction* replacement, + bool strictly) { const HUseList<HInstruction*>& uses = GetUses(); for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { HInstruction* user = it->GetUser(); size_t index = it->GetIndex(); // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). ++it; - if (dominator->StrictlyDominates(user)) { + if (dominator->Dominates(user, strictly)) { user->ReplaceInput(replacement, index); } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 42a9d95b9a..affd54ed72 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2098,9 +2098,13 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { return IsRemovable() && !HasUses(); } - // Does this instruction strictly dominate `other_instruction`? - // Returns false if this instruction and `other_instruction` are the same. + // Does this instruction dominate (strictly or in regular sense depending on 'strictly') + // `other_instruction`? + // Returns '!strictly' if this instruction and `other_instruction` are the same. // Aborts if this instruction and `other_instruction` are both phis. + bool Dominates(HInstruction* other_instruction, bool strictly) const; + + // Return 'Dominates(other_instruction, /*strictly*/ true)'. bool StrictlyDominates(HInstruction* other_instruction) const; int GetId() const { return id_; } @@ -2161,7 +2165,13 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { void SetLocations(LocationSummary* locations) { locations_ = locations; } void ReplaceWith(HInstruction* instruction); - void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement); + + // Replace all uses of the instruction which are dominated by 'dominator' with 'replacement'. + // 'strictly' determines whether strict or regular domination relation should be checked. + void ReplaceUsesDominatedBy(HInstruction* dominator, + HInstruction* replacement, + bool strictly = true); + void ReplaceInput(HInstruction* replacement, size_t index); // This is almost the same as doing `ReplaceWith()`. But in this helper, the diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 096349fd73..87dff8403b 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -109,6 +109,16 @@ class HVecOperation : public HVariableInputSizeInstruction { // Assumes vector nodes cannot be moved by default. Each concrete implementation // that can be moved should override this method and return true. + // + // Note: similar approach is used for instruction scheduling (if it is turned on for the target): + // by default HScheduler::IsSchedulable returns false for a particular HVecOperation. + // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see + // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also + // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction. + // + // Note: For newly introduced vector instructions HScheduler${ARCH}::IsSchedulingBarrier must be + // altered to return true if the instruction might reside outside the SIMD loop body since SIMD + // registers are not kept alive across vector loop boundaries (yet). bool CanBeMoved() const OVERRIDE { return false; } // Tests if all data of a vector node (vector length and packed type) is equal. diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 73c72fc57a..24b1a123ee 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1224,7 +1224,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1244,10 +1244,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = nullptr; info.cfi = jni_compiled_method.GetCfi(); - std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef<const debug::MethodDebugInfo>(&info, 1)); + mini_debug_info, + info); CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); } @@ -1352,7 +1355,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code); const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1372,10 +1375,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()); - std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector<uint8_t> elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef<const debug::MethodDebugInfo>(&info, 1)); + mini_debug_info, + info); CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index d84f14acc0..8bb124e066 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -544,7 +544,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst // the method is from the String class, the null loader is good enough. Handle<mirror::ClassLoader> loader(hs.NewHandle<mirror::ClassLoader>(nullptr)); ArtMethod* method = cl->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); + invoke->GetDexMethodIndex(), dex_cache, loader, /* referrer */ nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr); @@ -576,8 +576,8 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache, class_loader_.Get()); SetClassAsTypeInfo(instr, klass, is_exact); } diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index bb7c353bc2..dfa077f7de 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -462,6 +462,11 @@ class HScheduler { // containing basic block from being scheduled. // This method is used to restrict scheduling to instructions that we know are // safe to handle. + // + // For newly introduced instructions by default HScheduler::IsSchedulable returns false. + // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see + // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also + // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction. virtual bool IsSchedulable(const HInstruction* instruction) const; bool IsSchedulable(const HBasicBlock* block) const; diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index 32f161f26a..f71cb5b784 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -151,6 +151,20 @@ class HSchedulerARM64 : public HScheduler { #undef CASE_INSTRUCTION_KIND } + // Treat as scheduling barriers those vector instructions whose live ranges exceed the vectorized + // loop boundaries. This is a workaround for the lack of notion of SIMD register in the compiler; + // around a call we have to save/restore all live SIMD&FP registers (only lower 64 bits of + // SIMD&FP registers are callee saved) so don't reorder such vector instructions. + // + // TODO: remove this when a proper support of SIMD registers is introduced to the compiler. + bool IsSchedulingBarrier(const HInstruction* instr) const OVERRIDE { + return HScheduler::IsSchedulingBarrier(instr) || + instr->IsVecReduce() || + instr->IsVecExtractScalar() || + instr->IsVecSetScalars() || + instr->IsVecReplicateScalar(); + } + private: SchedulingLatencyVisitorARM64 arm64_latency_visitor_; DISALLOW_COPY_AND_ASSIGN(HSchedulerARM64); diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 4709fd0e9e..ee1d7c69bc 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -158,11 +158,10 @@ class VerifierDepsTest : public CommonCompilerTest { while (it.HasNextDirectMethod()) { ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *primary_dex_file_, it.GetMemberIndex(), dex_cache_handle, class_loader_handle, - nullptr, + /* referrer */ nullptr, it.GetMethodInvokeType(*class_def)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index a70e551ec0..7cb04f2aa8 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -2814,7 +2814,7 @@ class Dex2Oat FINAL { // Dex files we are compiling, does not include the class path dex files. std::vector<const DexFile*> dex_files_; std::string no_inline_from_string_; - CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; + CompactDexLevel compact_dex_level_ = kDefaultCompactDexLevel; std::vector<std::unique_ptr<linker::ElfWriter>> elf_writers_; std::vector<std::unique_ptr<linker::OatWriter>> oat_writers_; diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index 5fc33ddf8b..aa64b7d59e 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -67,7 +67,7 @@ class DebugInfoTask : public Task { void Run(Thread*) { result_ = debug::MakeMiniDebugInfo(isa_, instruction_set_features_, - rodata_section_size_, + kPageSize + rodata_section_size_, // .text address. text_section_size_, method_infos_); } @@ -173,6 +173,7 @@ template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::Start() { builder_->Start(); if (compiler_options_->GetGenerateBuildId()) { + builder_->GetBuildId()->AllocateVirtualMemory(builder_->GetBuildId()->GetSize()); builder_->WriteBuildIdSection(); } } @@ -225,9 +226,6 @@ void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) { template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::WriteDynamicSection() { - if (bss_size_ != 0u) { - builder_->GetBss()->WriteNoBitsSection(bss_size_); - } if (builder_->GetIsa() == InstructionSet::kMips || builder_->GetIsa() == InstructionSet::kMips64) { builder_->WriteMIPSabiflagsSection(); diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index f6ceb27b21..738bbf8e9d 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -1050,8 +1050,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, const DexFile::MethodId& method_id = dex_file.GetMethodId(i); if (method_id.class_idx_ != last_class_idx) { last_class_idx = method_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); if (last_class != nullptr && !KeepClass(last_class)) { last_class = nullptr; } @@ -1096,8 +1095,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, const DexFile::FieldId& field_id = dex_file.GetFieldId(i); if (field_id.class_idx_ != last_class_idx) { last_class_idx = field_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); if (last_class != nullptr && !KeepClass(last_class)) { last_class = nullptr; } @@ -1130,7 +1128,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, uint32_t stored_index = pair.index; ObjPtr<mirror::Class> klass = pair.object.Read(); if (klass == nullptr || i < stored_index) { - klass = class_linker->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader); + klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader); if (klass != nullptr) { DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass); stored_index = i; // For correct clearing below if not keeping the `klass`. diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index b163eeb7b7..d4fc59c6c2 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -1592,11 +1592,10 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ScopedObjectAccessUnchecked soa(self); StackHandleScope<1> hs(self); method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *dex_file_, it.GetMemberIndex(), hs.NewHandle(dex_cache), ScopedNullHandle<mirror::ClassLoader>(), - nullptr, + /* referrer */ nullptr, invoke_type); if (method == nullptr) { LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " @@ -1957,7 +1956,7 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { DCHECK(writer_->HasImage()); ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile()); ObjPtr<mirror::Class> type = - ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); + class_linker_->LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); CHECK(type != nullptr); return type; } @@ -2697,7 +2696,8 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { CompiledMethod* compiled_method = driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - uint32_t existing_debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); + CodeItemDebugInfoAccessor accessor(dex_file, code_item); + const uint32_t existing_debug_info_offset = accessor.DebugInfoOffset(); // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) // we will pretend the method has been quickened. bool existing_offset_out_of_bounds = diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index a7af193f0a..527a5b993c 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1203,10 +1203,16 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, bool is_static = (flags & kAccStatic) != 0; fprintf(gOutFile, " positions : \n"); uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, dumpPositionsCb, nullptr); + pDexFile->DecodeDebugPositionInfo(debug_info_offset, dumpPositionsCb, nullptr); fprintf(gOutFile, " locals : \n"); - pDexFile->DecodeDebugLocalInfo( - pCode, debug_info_offset, is_static, idx, dumpLocalsCb, nullptr); + pDexFile->DecodeDebugLocalInfo(pCode->registers_size_, + pCode->ins_size_, + pCode->insns_size_in_code_units_, + debug_info_offset, + is_static, + idx, + dumpLocalsCb, + nullptr); } /* diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 630eacba9b..4c13ed6410 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -122,7 +122,7 @@ static void dumpMethod(const DexFile* pDexFile, // Find the first line. int firstLine = -1; uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode); - pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, positionsCb, &firstLine); + pDexFile->DecodeDebugPositionInfo(debug_info_offset, positionsCb, &firstLine); // Method signature. const Signature signature = pDexFile->GetMethodSignature(pMethodId); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 1b4485b233..1a1d8cc76e 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -146,13 +146,10 @@ class OatSymbolizer FINAL { auto* rodata = builder_->GetRoData(); auto* text = builder_->GetText(); - auto* bss = builder_->GetBss(); const uint8_t* rodata_begin = oat_file_->Begin(); const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); - if (no_bits_) { - rodata->WriteNoBitsSection(rodata_size); - } else { + if (!no_bits_) { rodata->Start(); rodata->WriteFully(rodata_begin, rodata_size); rodata->End(); @@ -160,18 +157,12 @@ class OatSymbolizer FINAL { const uint8_t* text_begin = oat_file_->Begin() + rodata_size; const size_t text_size = oat_file_->End() - text_begin; - if (no_bits_) { - text->WriteNoBitsSection(text_size); - } else { + if (!no_bits_) { text->Start(); text->WriteFully(text_begin, text_size); text->End(); } - if (oat_file_->BssSize() != 0) { - bss->WriteNoBitsSection(oat_file_->BssSize()); - } - if (isa == InstructionSet::kMips || isa == InstructionSet::kMips64) { builder_->WriteMIPSabiflagsSection(); } diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc index 500a453f78..0b4906d798 100644 --- a/openjdkjvmti/ti_ddms.cc +++ b/openjdkjvmti/ti_ddms.cc @@ -49,14 +49,16 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, /*out*/jint* type_out, /*out*/jint* data_length_out, /*out*/jbyte** data_out) { - constexpr uint32_t kDdmHeaderSize = sizeof(uint32_t) * 2; - if (env == nullptr || data_in == nullptr || data_out == nullptr || data_length_out == nullptr) { + if (env == nullptr || type_out == nullptr || data_out == nullptr || data_length_out == nullptr) { return ERR(NULL_POINTER); - } else if (length_in < static_cast<jint>(kDdmHeaderSize)) { - // need to get type and length at least. + } else if (data_in == nullptr && length_in != 0) { + // Data-in shouldn't be null if we have data. return ERR(ILLEGAL_ARGUMENT); } + *data_length_out = 0; + *data_out = nullptr; + art::Thread* self = art::Thread::Current(); art::ScopedThreadStateChange(self, art::ThreadState::kNative); @@ -71,13 +73,15 @@ jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, return ERR(INTERNAL); } else { jvmtiError error = OK; - JvmtiUniquePtr<jbyte[]> ret = AllocJvmtiUniquePtr<jbyte[]>(env, out_data.size(), &error); - if (error != OK) { - return error; + if (!out_data.empty()) { + JvmtiUniquePtr<jbyte[]> ret = AllocJvmtiUniquePtr<jbyte[]>(env, out_data.size(), &error); + if (error != OK) { + return error; + } + memcpy(ret.get(), out_data.data(), out_data.size()); + *data_out = ret.release(); + *data_length_out = static_cast<jint>(out_data.size()); } - memcpy(ret.get(), out_data.data(), out_data.size()); - *data_out = ret.release(); - *data_length_out = static_cast<jint>(out_data.size()); return OK; } } diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index afd0723d0f..79a8cd6304 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -216,7 +216,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, { { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, - { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, true }, { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false } diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 448ce41d0f..4444853427 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -37,6 +37,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/mutex-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file_annotations.h" #include "dex_file_types.h" #include "events-inl.h" @@ -190,12 +191,17 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - const art::DexFile* dex_file = art_method->GetDexFile(); - const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); - // TODO code_item == nullptr means that the method is abstract (or native, but we check that + + const art::DexFile* const dex_file = art_method->GetDexFile(); + if (dex_file == nullptr) { + return ERR(ABSENT_INFORMATION); + } + + // TODO HasCodeItem == false means that the method is abstract (or native, but we check that // earlier). We should check what is returned by the RI in this situation since it's not clear // what the appropriate return value is from the spec. - if (dex_file == nullptr || code_item == nullptr) { + art::CodeItemDebugInfoAccessor accessor(art_method); + if (!accessor.HasCodeItem()) { return ERR(ABSENT_INFORMATION); } @@ -260,9 +266,10 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, }; LocalVariableContext context(env); - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (!dex_file->DecodeDebugLocalInfo(code_item, - debug_info_offset, + if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), art_method->IsStatic(), art_method->GetDexMethodIndex(), LocalVariableContext::Callback, @@ -462,7 +469,7 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, art::ArtMethod* art_method = art::jni::DecodeArtMethod(method); DCHECK(!art_method->IsRuntimeMethod()); - const art::DexFile::CodeItem* code_item; + art::CodeItemDebugInfoAccessor accessor; const art::DexFile* dex_file; { art::ScopedObjectAccess soa(art::Thread::Current()); @@ -477,15 +484,14 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, return ERR(NULL_POINTER); } - code_item = art_method->GetCodeItem(); + accessor = art::CodeItemDebugInfoAccessor(art_method); dex_file = art_method->GetDexFile(); - DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); + DCHECK(accessor.HasCodeItem()) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); } LineNumberContext context; - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); bool success = dex_file->DecodeDebugPositionInfo( - code_item, debug_info_offset, CollectLineNumbers, &context); + accessor.DebugInfoOffset(), CollectLineNumbers, &context); if (!success) { return ERR(ABSENT_INFORMATION); } @@ -613,8 +619,11 @@ class CommonLocalVariableClosure : public art::Closure { /*out*/art::Primitive::Type* type) REQUIRES(art::Locks::mutator_lock_) { const art::DexFile* dex_file = method->GetDexFile(); - const art::DexFile::CodeItem* code_item = method->GetCodeItem(); - if (dex_file == nullptr || code_item == nullptr) { + if (dex_file == nullptr) { + return ERR(OPAQUE_FRAME); + } + art::CodeItemDebugInfoAccessor accessor(method); + if (!accessor.HasCodeItem()) { return ERR(OPAQUE_FRAME); } @@ -653,9 +662,10 @@ class CommonLocalVariableClosure : public art::Closure { }; GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type); - uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item); - if (!dex_file->DecodeDebugLocalInfo(code_item, - debug_info_offset, + if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), method->IsStatic(), method->GetDexMethodIndex(), GetLocalVariableInfoContext::Callback, diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 941f9e908c..2b18577ed0 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -307,16 +307,10 @@ inline ObjPtr<mirror::Class> ArtField::LookupResolvedType() { if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - ObjPtr<mirror::DexCache> dex_cache = declaring_class->GetDexCache(); - const DexFile* const dex_file = dex_cache->GetDexFile(); - dex::TypeIndex type_idx = dex_file->GetFieldId(field_index).type_idx_; - ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_file, type_idx, dex_cache, declaring_class->GetClassLoader()); - DCHECK(!Thread::Current()->IsExceptionPending()); - } - return type.Ptr(); + ObjPtr<mirror::Class> type = Runtime::Current()->GetClassLinker()->LookupResolvedType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr<mirror::Class> ArtField::ResolveType() { @@ -325,15 +319,9 @@ inline ObjPtr<mirror::Class> ArtField::ResolveType() { if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - auto* dex_cache = declaring_class->GetDexCache(); - const DexFile* const dex_file = dex_cache->GetDexFile(); - dex::TypeIndex type_idx = dex_file->GetFieldId(field_index).type_idx_; - ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - type = class_linker->ResolveType(*dex_file, type_idx, declaring_class); - DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); - } + ObjPtr<mirror::Class> type = Runtime::Current()->GetClassLinker()->ResolveType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); return type; } diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 327081f67a..869394c388 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -106,23 +106,16 @@ inline uint32_t ArtMethod::GetDexMethodIndex() { inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); - ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader()); - } - return type.Ptr(); + ObjPtr<mirror::Class> type = + Runtime::Current()->GetClassLinker()->LookupResolvedType(type_idx, this); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr<mirror::Class> ArtMethod::ResolveClassFromTypeIndex(dex::TypeIndex type_idx) { - ObjPtr<mirror::DexCache> dex_cache = GetDexCache(); - ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type.Ptr(); + ObjPtr<mirror::Class> type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); + return type; } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -305,9 +298,7 @@ inline const DexFile::ClassDef& ArtMethod::GetClassDef() { inline const char* ArtMethod::GetReturnTypeDescriptor() { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); - const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)); + return dex_file->GetTypeDescriptor(dex_file->GetTypeId(GetReturnTypeIndex())); } inline Primitive::Type ArtMethod::GetReturnTypePrimitive() { diff --git a/runtime/base/macros.h b/runtime/base/macros.h index 6cd7d60253..512e5ce651 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -59,6 +59,10 @@ template<typename T> ART_FRIEND_TEST(test_set_name, individual_test) #define QUOTE(x) #x #define STRINGIFY(x) QUOTE(x) +// Append tokens after evaluating. +#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b +#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b) + #ifndef NDEBUG #define ALWAYS_INLINE #else diff --git a/runtime/cdex/compact_dex_level.h b/runtime/cdex/compact_dex_level.h index b824462bf0..5aec00195d 100644 --- a/runtime/cdex/compact_dex_level.h +++ b/runtime/cdex/compact_dex_level.h @@ -17,6 +17,9 @@ #ifndef ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ #define ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ +#include <string> + +#include "base/macros.h" #include "dex_file.h" namespace art { @@ -29,6 +32,19 @@ enum class CompactDexLevel { kCompactDexLevelFast, }; +#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL +#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified. +#else +#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast +#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone + +#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \ + ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \ + ART_DEFAULT_COMPACT_DEX_LEVEL) + +static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT; +#endif + } // namespace art #endif // ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 0b9bf225d4..90f478f5f4 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -373,7 +373,7 @@ class ScopedCheck { if (f == nullptr) { return false; } - if (c != f->GetDeclaringClass()) { + if (!f->GetDeclaringClass()->IsAssignableFrom(c)) { AbortF("static jfieldID %p not valid for class %s", fid, mirror::Class::PrettyClass(c).c_str()); return false; @@ -710,7 +710,7 @@ class ScopedCheck { return false; } ObjPtr<mirror::Class> c = o->AsClass(); - if (c != field->GetDeclaringClass()) { + if (!field->GetDeclaringClass()->IsAssignableFrom(c)) { AbortF("attempt to access static field %s with an incompatible class argument of %s: %p", field->PrettyField().c_str(), mirror::Class::PrettyDescriptor(c).c_str(), fid); return false; diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index 2d8d4b41a0..4b317f886f 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -61,16 +61,27 @@ inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, return array_class.Ptr(); } -inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType( - dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) { - ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (type == nullptr) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader); +inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> referrer) { + if (kObjPtrPoisoning) { + StackHandleScope<1> hs(Thread::Current()); + HandleWrapperObjPtr<mirror::Class> referrer_wrapper = hs.NewHandleWrapper(&referrer); + Thread::Current()->PoisonObjectPointers(); } - return type; + if (kIsDebugBuild) { + Thread::Current()->AssertNoPendingException(); + } + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr<mirror::Class> resolved_type = + referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx); + if (resolved_type == nullptr) { + StackHandleScope<2> hs(Thread::Current()); + Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + resolved_type = DoResolveType(type_idx, h_dex_cache, class_loader); + } + return resolved_type; } inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, @@ -79,18 +90,67 @@ inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr<mirror::Class> resolved_type = + referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referring_class->GetClassLoader())); + resolved_type = DoResolveType(type_idx, dex_cache, class_loader); } return resolved_type; } +inline ObjPtr<mirror::Class> ClassLinker::ResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + DCHECK(dex_cache != nullptr); + Thread::PoisonObjectPointersIfDebug(); + ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx); + if (resolved == nullptr) { + resolved = DoResolveType(type_idx, dex_cache, class_loader); + } + return resolved; +} + +inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> referrer) { + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr<mirror::Class> type = + referrer->GetDexCache<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ArtMethod* referrer) { + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr<mirror::Class> type = + referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType( + dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, dex_cache, class_loader); + } + return type; +} + template <bool kThrowOnError, typename ClassGetter> inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache, InvokeType type, @@ -148,10 +208,9 @@ inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_c dex_cache, type, [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = *dex_cache->GetDexFile(); - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); ObjPtr<mirror::Class> klass = - LookupResolvedType(dex_file, method_id.class_idx_, dex_cache, class_loader); + LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); DCHECK(klass != nullptr); return klass; }); @@ -187,6 +246,8 @@ inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* // lookup in the context of the original method from where it steals the code. // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); + // We do not need the read barrier for getting the DexCache for the initial resolved method + // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod( method_idx, image_pointer_size_); if (resolved_method == nullptr) { @@ -228,6 +289,8 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); Thread::PoisonObjectPointersIfDebug(); + // We do not need the read barrier for getting the DexCache for the initial resolved method + // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod( method_idx, image_pointer_size_); DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod()); @@ -237,9 +300,7 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, StackHandleScope<2> hs(self); Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile* dex_file = h_dex_cache->GetDexFile(); - resolved_method = ResolveMethod<kResolveMode>(*dex_file, - method_idx, + resolved_method = ResolveMethod<kResolveMode>(method_idx, h_dex_cache, h_class_loader, referrer, @@ -280,10 +341,13 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { - ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache(); - ArtField* field = dex_cache->GetResolvedField(field_idx, image_pointer_size_); + // We do not need the read barrier for getting the DexCache for the initial resolved field + // lookup as both from-space and to-space copies point to the same native resolved fields array. + ArtField* field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField( + field_idx, image_pointer_size_); if (field == nullptr) { - field = LookupResolvedField(field_idx, dex_cache, referrer->GetClassLoader(), is_static); + ObjPtr<mirror::ClassLoader> class_loader = referrer->GetDeclaringClass()->GetClassLoader(); + field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static); } return field; } @@ -292,13 +356,15 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { Thread::PoisonObjectPointersIfDebug(); - ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass(); - ArtField* resolved_field = - referrer->GetDexCache()->GetResolvedField(field_idx, image_pointer_size_); + // We do not need the read barrier for getting the DexCache for the initial resolved field + // lookup as both from-space and to-space copies point to the same native resolved fields array. + ArtField* resolved_field = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedField( + field_idx, image_pointer_size_); if (UNLIKELY(resolved_field == nullptr)) { StackHandleScope<2> hs(Thread::Current()); + ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass(); Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referring_class->GetClassLoader())); resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static); // Note: We cannot check here to see whether we added the field to the cache. The type // might be an erroneous class, which results in it being hidden from us. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 093ec6540c..55fa6328f5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5466,7 +5466,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF return false; } - ObjPtr<mirror::Class> super_class = ResolveType(dex_file, super_class_idx, klass.Get()); + ObjPtr<mirror::Class> super_class = ResolveType(super_class_idx, klass.Get()); if (super_class == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -5485,7 +5485,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF if (interfaces != nullptr) { for (size_t i = 0; i < interfaces->Size(); i++) { dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; - ObjPtr<mirror::Class> interface = ResolveType(dex_file, idx, klass.Get()); + ObjPtr<mirror::Class> interface = ResolveType(idx, klass.Get()); if (interface == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -7762,74 +7762,56 @@ ObjPtr<mirror::String> ClassLinker::LookupString(dex::StringIndex string_idx, return string; } -ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) { - ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx); - if (type == nullptr) { - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; - if (descriptor[1] == '\0') { - // only the descriptors of primitive types should be 1 character long, also avoid class lookup - // for primitive classes that aren't backed by dex files. - type = FindPrimitiveClass(descriptor[0]); +ObjPtr<mirror::Class> ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const char* descriptor = dex_file.StringByTypeIdx(type_idx); + DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; + ObjPtr<mirror::Class> type = nullptr; + if (descriptor[1] == '\0') { + // only the descriptors of primitive types should be 1 character long, also avoid class lookup + // for primitive classes that aren't backed by dex files. + type = FindPrimitiveClass(descriptor[0]); + } else { + Thread* const self = Thread::Current(); + DCHECK(self != nullptr); + const size_t hash = ComputeModifiedUtf8Hash(descriptor); + // Find the class in the loaded classes table. + type = LookupClass(self, descriptor, hash, class_loader.Ptr()); + } + if (type != nullptr) { + if (type->IsResolved()) { + dex_cache->SetResolvedType(type_idx, type); } else { - Thread* const self = Thread::Current(); - DCHECK(self != nullptr); - const size_t hash = ComputeModifiedUtf8Hash(descriptor); - // Find the class in the loaded classes table. - type = LookupClass(self, descriptor, hash, class_loader.Ptr()); - } - if (type != nullptr) { - if (type->IsResolved()) { - dex_cache->SetResolvedType(type_idx, type); - } else { - type = nullptr; - } + type = nullptr; } } - DCHECK(type == nullptr || type->IsResolved()); return type; } -ObjPtr<mirror::Class> ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::Class> referrer) { - StackHandleScope<2> hs(Thread::Current()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); - return ResolveType(dex_file, type_idx, dex_cache, class_loader); -} - -ObjPtr<mirror::Class> ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader) { - DCHECK(dex_cache != nullptr); - Thread::PoisonObjectPointersIfDebug(); - ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx); - if (resolved == nullptr) { - Thread* self = Thread::Current(); - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - resolved = FindClass(self, descriptor, class_loader); - if (resolved != nullptr) { - // TODO: we used to throw here if resolved's class loader was not the - // boot class loader. This was to permit different classes with the - // same name to be loaded simultaneously by different loaders - dex_cache->SetResolvedType(type_idx, resolved); - } else { - CHECK(self->IsExceptionPending()) - << "Expected pending exception for failed resolution of: " << descriptor; - // Convert a ClassNotFoundException to a NoClassDefFoundError. - StackHandleScope<1> hs(self); - Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException())); - if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { - DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. - self->ClearException(); - ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); - self->GetException()->SetCause(cause.Get()); - } +ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) { + Thread* self = Thread::Current(); + const char* descriptor = dex_cache->GetDexFile()->StringByTypeIdx(type_idx); + ObjPtr<mirror::Class> resolved = FindClass(self, descriptor, class_loader); + if (resolved != nullptr) { + // TODO: we used to throw here if resolved's class loader was not the + // boot class loader. This was to permit different classes with the + // same name to be loaded simultaneously by different loaders + dex_cache->SetResolvedType(type_idx, resolved); + } else { + CHECK(self->IsExceptionPending()) + << "Expected pending exception for failed resolution of: " << descriptor; + // Convert a ClassNotFoundException to a NoClassDefFoundError. + StackHandleScope<1> hs(self); + Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException())); + if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. + self->ClearException(); + ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); + self->GetException()->SetCause(cause.Get()); } } DCHECK((resolved == nullptr) || resolved->IsResolved()) @@ -7938,8 +7920,7 @@ std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* clas } template <ClassLinker::ResolveMode kResolveMode> -ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, @@ -7957,12 +7938,13 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); ObjPtr<mirror::Class> klass = nullptr; if (valid_dex_cache_method) { // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); - klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get()); if (UNLIKELY(klass == nullptr)) { const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_); LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: " @@ -7973,7 +7955,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } } else { // The method was not in the DexCache, resolve the declaring class. - klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8048,8 +8030,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } } -ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); @@ -8060,9 +8041,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, return resolved; } // Fail, get the declaring class. - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = - ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); + ObjPtr<mirror::Class> klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { Thread::Current()->AssertPendingException(); return nullptr; @@ -8084,7 +8064,7 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(field_id.class_idx_); if (klass == nullptr) { - klass = LookupResolvedType(dex_file, field_id.class_idx_, dex_cache, class_loader); + klass = LookupResolvedType(field_id.class_idx_, dex_cache, class_loader); } if (klass == nullptr) { // The class has not been resolved yet, so the field is also unresolved. @@ -8126,7 +8106,7 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* const self = Thread::Current(); - ObjPtr<mirror::Class> klass = ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader); + ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8167,7 +8147,7 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* self = Thread::Current(); - ObjPtr<mirror::Class> klass(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader)); + ObjPtr<mirror::Class> klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8203,7 +8183,7 @@ ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType( const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); Handle<mirror::Class> return_type(hs.NewHandle( - ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader))); + ResolveType(proto_id.return_type_idx_, dex_cache, class_loader))); if (return_type == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8229,7 +8209,7 @@ ObjPtr<mirror::MethodType> ClassLinker::ResolveMethodType( MutableHandle<mirror::Class> param_class = hs.NewHandle<mirror::Class>(nullptr); for (; it.HasNext(); it.Next()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader)); + param_class.Assign(ResolveType(type_idx, dex_cache, class_loader)); if (param_class == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8431,8 +8411,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( // the invocation type to determine if the method is private. We // then resolve again specifying the intended invocation type to // force the appropriate checks. - target_method = ResolveMethodWithoutInvokeType(*dex_file, - method_handle.field_or_method_idx_, + target_method = ResolveMethodWithoutInvokeType(method_handle.field_or_method_idx_, hs.NewHandle(referrer->GetDexCache()), hs.NewHandle(referrer->GetClassLoader())); if (UNLIKELY(target_method == nullptr)) { @@ -8515,7 +8494,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( DexFileParameterIterator it(*dex_file, target_method->GetPrototype()); while (it.HasNext()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - ObjPtr<mirror::Class> klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader); + ObjPtr<mirror::Class> klass = ResolveType(type_idx, dex_cache, class_loader); if (nullptr == klass) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -9033,14 +9012,12 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { // Instantiate ResolveMethod. template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, InvokeType type); template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - const DexFile& dex_file, uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 6a5768ec27..10562f0890 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -255,45 +255,50 @@ class ClassLinker { ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identify the - // target DexCache and ClassLoader to use for resolution. - ObjPtr<mirror::Class> ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::Class> referrer) + // Resolve a Type with the given index from the DexFile associated with the given `referrer`, + // storing the result in the DexCache. The `referrer` is used to identify the target DexCache + // and ClassLoader to use for resolution. + ObjPtr<mirror::Class> ResolveType(dex::TypeIndex type_idx, ObjPtr<mirror::Class> referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identify the - // target DexCache and ClassLoader to use for resolution. + // Resolve a type with the given index from the DexFile associated with the given `referrer`, + // storing the result in the DexCache. The `referrer` is used to identify the target DexCache + // and ClassLoader to use for resolution. ObjPtr<mirror::Class> ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search - // for the type, since it may be referenced from but not contained within the given DexFile. - ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - static ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, - ObjPtr<mirror::DexCache> dex_cache, - ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Resolve a type with the given ID from the DexFile, storing the - // result in DexCache. The ClassLoader is used to search for the - // type, since it may be referenced from but not contained within - // the given DexFile. - ObjPtr<mirror::Class> ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, + // Resolve a type with the given index from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLoader is used to search for + // the type, since it may be referenced from but not contained within the DexFile. + ObjPtr<mirror::Class> ResolveType(dex::TypeIndex type_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Look up a resolved type with the given index from the DexFile associated with the given + // `referrer`, storing the result in the DexCache. The `referrer` is used to identify the + // target DexCache and ClassLoader to use for lookup. + ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::Class> referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Look up a resolved type with the given index from the DexFile associated with the given + // `referrer`, storing the result in the DexCache. The `referrer` is used to identify the + // target DexCache and ClassLoader to use for lookup. + ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Look up a resolved type with the given index from the DexFile associated with the given + // DexCache and ClassLoader. The ClassLoader is used to search for the type, since it may + // be referenced from but not contained within the DexFile. + ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + // Determine whether a dex cache result should be trusted, or an IncompatibleClassChangeError // check and IllegalAccessError check should be performed even after a hit. enum class ResolveMode { // private. @@ -307,14 +312,12 @@ class ClassLinker { ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a method with a given ID from the DexFile, storing the - // result in DexCache. The ClassLinker and ClassLoader are used as - // in ResolveType. What is unique is the method type argument which - // is used to determine if this method is a direct, static, or - // virtual method. + // Resolve a method with a given ID from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are + // used as in ResolveType. What is unique is the method type argument which is used to + // determine if this method is a direct, static, or virtual method. template <ResolveMode kResolveMode> - ArtMethod* ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethod(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, @@ -330,8 +333,7 @@ class ClassLinker { ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - ArtMethod* ResolveMethodWithoutInvokeType(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethodWithoutInvokeType(uint32_t method_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) @@ -879,6 +881,19 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + // Implementation of LookupResolvedType() called when the type was not found in the dex cache. + ObjPtr<mirror::Class> DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr<mirror::DexCache> dex_cache, + ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Implementation of ResolveType() called when the type was not found in the dex cache. + ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx, + Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded // by the given 'class_loader'. Uses the provided hash for the descriptor. mirror::Class* LookupClass(Thread* self, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index b625c40fc3..246f89e5cc 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -954,15 +954,14 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache(); - const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache, class_loader.Get()), klass); // Zero out the resolved type and make sure LookupResolvedType still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache, class_loader.Get()), klass); } @@ -983,7 +982,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { dex::TypeIndex array_idx = dex_file.GetIndexForTypeId(*array_id); // Check that the array class wasn't resolved yet. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), ObjPtr<mirror::Class>(nullptr)); // Resolve the array class we want to test. ObjPtr<mirror::Class> array_klass @@ -991,13 +990,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { ASSERT_OBJ_PTR_NE(array_klass, ObjPtr<mirror::Class>(nullptr)); // Test that LookupResolvedType() finds the array class. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), array_klass); // Zero out the resolved type and make sure LookupResolvedType() still finds it. dex_cache->ClearResolvedType(array_idx); EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), array_klass); } @@ -1012,15 +1011,14 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr<mirror::Class>(nullptr)); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Force initialization to turn the class erroneous. bool initialized = class_linker_->EnsureInitialized(soa.Self(), @@ -1032,13 +1030,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { soa.Self()->ClearException(); // Check that the LookupResolvedType() can still find the resolved type. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType() still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); } diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h index d04849d09a..4f4d8cc86e 100644 --- a/runtime/code_item_accessors-inl.h +++ b/runtime/code_item_accessors-inl.h @@ -22,6 +22,7 @@ #include "art_method-inl.h" #include "cdex/compact_dex_file.h" #include "dex_file-inl.h" +#include "oat_file.h" #include "standard_dex_file.h" namespace art { @@ -37,7 +38,7 @@ inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& c } inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, - const DexFile::CodeItem* code_item) { + const DexFile::CodeItem* code_item) { DCHECK(dex_file != nullptr); DCHECK(code_item != nullptr); if (dex_file->IsCompactDexFile()) { @@ -150,6 +151,31 @@ inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_de return index != -1 ? &try_items.begin()[index] : nullptr; } +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) + : CodeItemDebugInfoAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + if (code_item == nullptr) { + return; + } + debug_info_offset_ = OatFile::GetDebugInfoOffset(*dex_file, code_item); + if (dex_file->IsCompactDexFile()) { + Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); + } +} + +inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + +inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h index aa1305acad..a089a276de 100644 --- a/runtime/code_item_accessors.h +++ b/runtime/code_item_accessors.h @@ -132,6 +132,30 @@ class CodeItemDataAccessor : public CodeItemInstructionAccessor { uint16_t tries_size_; }; +// Abstract accesses to code item data including debug info offset. More heavy weight than the other +// helpers. +class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { + public: + CodeItemDebugInfoAccessor() = default; + + // Handles null code items, but not null dex files. + ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item); + + ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); + + uint32_t DebugInfoOffset() const { + return debug_info_offset_; + } + + protected: + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + + private: + uint32_t debug_info_offset_ = 0u; +}; + } // namespace art #endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 13029fb958..b5ae09f701 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1661,16 +1661,16 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan } }; ArtMethod* m = FromMethodId(method_id); - const DexFile::CodeItem* code_item = m->GetCodeItem(); + CodeItemDebugInfoAccessor accessor(m); uint64_t start, end; - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { DCHECK(m->IsNative() || m->IsProxyMethod()); start = -1; end = -1; } else { start = 0; // Return the index of the last instruction - end = code_item->insns_size_in_code_units_ - 1; + end = accessor.InsnsSizeInCodeUnits() - 1; } expandBufAdd8BE(pReply, start); @@ -1684,10 +1684,10 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan context.numItems = 0; context.pReply = pReply; - if (code_item != nullptr) { - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugPositionInfo( - code_item, debug_info_offset, DebugCallbackContext::Callback, &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems); @@ -1727,6 +1727,7 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi } }; ArtMethod* m = FromMethodId(method_id); + CodeItemDebugInfoAccessor accessor(m); // arg_count considers doubles and longs to take 2 units. // variable_count considers everything to take 1 unit. @@ -1742,12 +1743,15 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi context.variable_count = 0; context.with_generic = with_generic; - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item != nullptr) { - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugLocalInfo( - code_item, debug_info_offset, m->IsStatic(), m->GetDexMethodIndex(), - DebugCallbackContext::Callback, &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), + m->IsStatic(), + m->GetDexMethodIndex(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count); @@ -3836,9 +3840,9 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Find the dex_pc values that correspond to the current line, for line-based single-stepping. struct DebugCallbackContext { DebugCallbackContext(SingleStepControl* single_step_control_cb, - int32_t line_number_cb, const DexFile::CodeItem* code_item) + int32_t line_number_cb, uint32_t num_insns_in_code_units) : single_step_control_(single_step_control_cb), line_number_(line_number_cb), - code_item_(code_item), last_pc_valid(false), last_pc(0) { + num_insns_in_code_units_(num_insns_in_code_units), last_pc_valid(false), last_pc(0) { } static bool Callback(void* raw_context, const DexFile::PositionInfo& entry) { @@ -3864,8 +3868,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize ~DebugCallbackContext() { // If the line number was the last in the position table... if (last_pc_valid) { - size_t end = code_item_->insns_size_in_code_units_; - for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) { + for (uint32_t dex_pc = last_pc; dex_pc < num_insns_in_code_units_; ++dex_pc) { single_step_control_->AddDexPc(dex_pc); } } @@ -3873,7 +3876,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize SingleStepControl* const single_step_control_; const int32_t line_number_; - const DexFile::CodeItem* const code_item_; + const uint32_t num_insns_in_code_units_; bool last_pc_valid; uint32_t last_pc; }; @@ -3892,11 +3895,11 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Note: if the thread is not running Java code (pure native thread), there is no "current" // method on the stack (and no line number either). if (m != nullptr && !m->IsNative()) { - const DexFile::CodeItem* const code_item = m->GetCodeItem(); - DebugCallbackContext context(single_step_control, line_number, code_item); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item); - m->GetDexFile()->DecodeDebugPositionInfo( - code_item, debug_info_offset, DebugCallbackContext::Callback, &context); + CodeItemDebugInfoAccessor accessor(m); + DebugCallbackContext context(single_step_control, line_number, accessor.InsnsSizeInCodeUnits()); + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } // Activate single-step in the thread. @@ -4376,15 +4379,20 @@ bool Dbg::DdmHandleChunk(JNIEnv* env, replyData.get(), offset, length); - if (length == 0 || replyData.get() == nullptr) { - return false; - } - out_data->resize(length); env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast<jbyte*>(out_data->data())); + + if (env->ExceptionCheck()) { + LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", + type); + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + return true; } @@ -4418,7 +4426,7 @@ bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pRep std::vector<uint8_t> out_data; uint32_t out_type = 0; request->Skip(request_length); - if (!DdmHandleChunk(env, type, data, &out_type, &out_data)) { + if (!DdmHandleChunk(env, type, data, &out_type, &out_data) || out_data.empty()) { return false; } const uint32_t kDdmHeaderSize = 8; diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 4025a1a0f9..a6f762120c 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -384,13 +384,16 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, } template<typename NewLocalCallback> -bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, +bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, + uint32_t ins_size, + uint32_t insns_size_in_code_units, uint32_t debug_info_offset, bool is_static, uint32_t method_idx, NewLocalCallback new_local_callback, void* context) const { - if (code_item == nullptr) { + const uint8_t* const stream = GetDebugInfoStream(debug_info_offset); + if (stream == nullptr) { return false; } std::vector<const char*> arg_descriptors; @@ -398,15 +401,15 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, for (; it.HasNext(); it.Next()) { arg_descriptors.push_back(it.GetDescriptor()); } - return DecodeDebugLocalInfo(GetDebugInfoStream(debug_info_offset), + return DecodeDebugLocalInfo(stream, GetLocation(), GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)), arg_descriptors, this->PrettyMethod(method_idx), is_static, - code_item->registers_size_, - code_item->ins_size_, - code_item->insns_size_in_code_units_, + registers_size, + ins_size, + insns_size_in_code_units, [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); }, @@ -487,13 +490,9 @@ bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, } template<typename DexDebugNewPosition> -bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, - uint32_t debug_info_offset, +bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const { - if (code_item == nullptr) { - return false; - } return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 561b3678b4..de3af8a289 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -956,7 +956,9 @@ class DexFile { NewLocalCallback new_local, void* context); template<typename NewLocalCallback> - bool DecodeDebugLocalInfo(const CodeItem* code_item, + bool DecodeDebugLocalInfo(uint32_t registers_size, + uint32_t ins_size, + uint32_t insns_size_in_code_units, uint32_t debug_info_offset, bool is_static, uint32_t method_idx, @@ -970,8 +972,7 @@ class DexFile { DexDebugNewPosition position_functor, void* context); template<typename DexDebugNewPosition> - bool DecodeDebugPositionInfo(const CodeItem* code_item, - uint32_t debug_info_offset, + bool DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const; diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index eaf31f308f..72b18fb420 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -343,8 +343,7 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** StackHandleScope<4> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> annotation_class(hs.NewHandle( - class_linker->ResolveType(klass.GetDexFile(), - dex::TypeIndex(type_index), + class_linker->ResolveType(dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())))); if (annotation_class == nullptr) { @@ -474,7 +473,6 @@ bool ProcessAnnotationValue(const ClassData& klass, dex::TypeIndex type_index(index); StackHandleScope<2> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveType( - klass.GetDexFile(), type_index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -501,7 +499,6 @@ bool ProcessAnnotationValue(const ClassData& klass, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(self); ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -783,7 +780,6 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( Thread* self = Thread::Current(); StackHandleScope<2> hs(self); ObjPtr<mirror::Class> resolved_class = class_linker->ResolveType( - klass.GetDexFile(), dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -1398,7 +1394,6 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) { } StackHandleScope<2> hs(Thread::Current()); ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType( - data.GetDexFile(), annotation_value.value_.GetI(), hs.NewHandle(data.GetDexCache()), hs.NewHandle(data.GetClassLoader())); @@ -1564,14 +1559,12 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re return -2; } - const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); - DCHECK(code_item != nullptr) << method->PrettyMethod() << " " << dex_file->GetLocation(); + CodeItemDebugInfoAccessor accessor(method); + DCHECK(accessor.HasCodeItem()) << method->PrettyMethod() << " " << dex_file->GetLocation(); // A method with no line number info should return -1 DexFile::LineNumFromPcContext context(rel_pc, -1); - uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item); - dex_file->DecodeDebugPositionInfo( - code_item, debug_info_offset, DexFile::LineNumForPcCb, &context); + dex_file->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), DexFile::LineNumForPcCb, &context); return context.line_num_; } @@ -1596,8 +1589,7 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c break; } case kType: { - ObjPtr<mirror::Class> resolved = linker_->ResolveType(dex_file_, - dex::TypeIndex(jval_.i), + ObjPtr<mirror::Class> resolved = linker_->ResolveType(dex::TypeIndex(jval_.i), dex_cache_, class_loader_); field->SetObject<kTransactionActive>(field->GetDeclaringClass(), resolved); diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 14c36b4538..69055041e5 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -731,7 +731,14 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { const DexFile::ClassDef& class_def = raw->GetClassDef(0); const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item); - ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, debug_info_offset, true, 1, Callback, nullptr)); + ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item->registers_size_, + code_item->ins_size_, + code_item->insns_size_in_code_units_, + debug_info_offset, + true, + 1, + Callback, + nullptr)); } } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index fa3c027db8..9e5085067c 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -682,7 +682,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } else if (type == kSuper) { // TODO This lookup is rather slow. dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; - ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType( + ObjPtr<mirror::Class> method_reference_class = linker->LookupResolvedType( method_type_idx, dex_cache, referrer->GetClassLoader()); if (method_reference_class == nullptr) { // Need to do full type resolution... diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 0a76cddf5e..ca5b79921c 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1250,17 +1250,8 @@ extern "C" const void* artQuickResolutionTrampoline( } else { DCHECK_EQ(invoke_type, kSuper); CHECK(caller != nullptr) << invoke_type; - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache( - hs.NewHandle(caller->GetDeclaringClass()->GetDexCache())); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader())); - // TODO Maybe put this into a mirror::Class function. ObjPtr<mirror::Class> ref_class = linker->LookupResolvedType( - *dex_cache->GetDexFile(), - dex_cache->GetDexFile()->GetMethodId(called_method.index).class_idx_, - dex_cache.Get(), - class_loader.Get()); + caller->GetDexFile()->GetMethodId(called_method.index).class_idx_, caller); if (ref_class->IsInterface()) { called = ref_class->FindVirtualMethodForInterfaceSuper(called, kRuntimePointerSize); } else { @@ -2580,9 +2571,8 @@ extern "C" uintptr_t artInvokePolymorphic( const Instruction& inst = code->InstructionAt(dex_pc); DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC || inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); - const DexFile* dex_file = caller_method->GetDexFile(); const uint32_t proto_idx = inst.VRegH(); - const char* shorty = dex_file->GetShorty(proto_idx); + const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); const size_t shorty_length = strlen(shorty); static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 74813b4dd1..bcfc68c4a6 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1587,7 +1587,9 @@ std::unique_ptr<ImageSpace> ImageSpace::CreateBootImage(const char* image_locati if (!Runtime::Current()->IsImageDex2OatEnabled()) { local_error_msg = "Patching disabled."; } else if (secondary_image) { - local_error_msg = "Cannot patch a secondary image."; + // We really want a working image. Prune and restart. + PruneDalvikCache(image_isa); + _exit(1); } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) { bool patch_success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 4d7a576c06..122d1a816b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -1143,8 +1143,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kType: { dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i)); - ObjPtr<mirror::Class> ref = - class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader); + ObjPtr<mirror::Class> ref = class_linker->ResolveType(idx, dex_cache, class_loader); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 783d05df34..278bc57412 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -648,6 +648,10 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // We do not want to compile such methods. return; } + if (hot_method_threshold_ == 0) { + // Tests might request JIT on first use (compiled synchronously in the interpreter). + return; + } DCHECK(thread_pool_ != nullptr); DCHECK_GT(warm_method_threshold_, 0); DCHECK_GT(hot_method_threshold_, warm_method_threshold_); diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 1ecfe7cb76..efeff0a378 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -1783,66 +1783,115 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \ } while (false) +#define TEST_PRIMITIVE_FIELD_FOR_CLASS(cname) \ + do { \ + Thread::Current()->TransitionFromSuspendedToRunnable(); \ + LoadDex("AllFields"); \ + bool started = runtime_->Start(); \ + ASSERT_TRUE(started); \ + jclass c = env_->FindClass(cname); \ + ASSERT_NE(c, nullptr); \ + jobject o = env_->AllocObject(c); \ + ASSERT_NE(o, nullptr); \ + \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b'); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2); \ + \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b'); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2); \ + } while (false) TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) { + TEST_PRIMITIVE_FIELD_FOR_CLASS("AllFields"); +} + +TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField_Subclass) { + TEST_PRIMITIVE_FIELD_FOR_CLASS("AllFieldsSub"); +} + +#define EXPECT_UNRELATED_FIELD_FAILURE(type, field_name, sig, value1) \ + do { \ + jfieldID fid = env_->GetStaticFieldID(c, field_name, sig); \ + EXPECT_NE(fid, nullptr); \ + CheckJniAbortCatcher jni_abort_catcher; \ + env_->Get ## type ## Field(uc, fid); \ + jni_abort_catcher.Check("not valid for an object of class"); \ + env_->Set ## type ## Field(uc, fid, value1); \ + jni_abort_catcher.Check("not valid for an object of class"); \ + } while (false) + +TEST_F(JniInternalTest, GetField_SetField_unrelated) { Thread::Current()->TransitionFromSuspendedToRunnable(); LoadDex("AllFields"); bool started = runtime_->Start(); ASSERT_TRUE(started); - jclass c = env_->FindClass("AllFields"); ASSERT_NE(c, nullptr); - jobject o = env_->AllocObject(c); - ASSERT_NE(o, nullptr); - - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b'); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2); - - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b'); - EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0); - EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2); + jclass uc = env_->FindClass("AllFieldsUnrelated"); + ASSERT_NE(uc, nullptr); + bool old_check_jni = vm_->SetCheckJniEnabled(true); + EXPECT_UNRELATED_FIELD_FAILURE(Boolean, "sZ", "Z", JNI_TRUE); + EXPECT_UNRELATED_FIELD_FAILURE(Byte, "sB", "B", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Char, "sC", "C", 'a'); + EXPECT_UNRELATED_FIELD_FAILURE(Double, "sD", "D", 1.0); + EXPECT_UNRELATED_FIELD_FAILURE(Float, "sF", "F", 1.0); + EXPECT_UNRELATED_FIELD_FAILURE(Int, "sI", "I", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Long, "sJ", "J", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Short, "sS", "S", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Object, "sObject", "Ljava/lang/Object;", c); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } -TEST_F(JniInternalTest, GetObjectField_SetObjectField) { - Thread::Current()->TransitionFromSuspendedToRunnable(); - LoadDex("AllFields"); - runtime_->Start(); +#define TEST_OBJECT_FIELD_FOR_CLASS(cname) \ + do { \ + Thread::Current()->TransitionFromSuspendedToRunnable(); \ + LoadDex("AllFields"); \ + runtime_->Start(); \ + \ + jclass c = env_->FindClass(cname); \ + ASSERT_NE(c, nullptr); \ + jobject o = env_->AllocObject(c); \ + ASSERT_NE(o, nullptr); \ + \ + jstring s1 = env_->NewStringUTF("hello"); \ + ASSERT_NE(s1, nullptr); \ + jstring s2 = env_->NewStringUTF("world"); \ + ASSERT_NE(s2, nullptr); \ + \ + jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); \ + ASSERT_NE(s_fid, nullptr); \ + jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); \ + ASSERT_NE(i_fid, nullptr); \ + \ + env_->SetStaticObjectField(c, s_fid, s1); \ + ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); \ + env_->SetStaticObjectField(c, s_fid, s2); \ + ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid))); \ + \ + env_->SetObjectField(o, i_fid, s1); \ + ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid))); \ + env_->SetObjectField(o, i_fid, s2); \ + ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); \ + } while (false) - jclass c = env_->FindClass("AllFields"); - ASSERT_NE(c, nullptr); - jobject o = env_->AllocObject(c); - ASSERT_NE(o, nullptr); +TEST_F(JniInternalTest, GetObjectField_SetObjectField) { + TEST_OBJECT_FIELD_FOR_CLASS("AllFields"); +} - jstring s1 = env_->NewStringUTF("hello"); - ASSERT_NE(s1, nullptr); - jstring s2 = env_->NewStringUTF("world"); - ASSERT_NE(s2, nullptr); - - jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); - ASSERT_NE(s_fid, nullptr); - jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); - ASSERT_NE(i_fid, nullptr); - - env_->SetStaticObjectField(c, s_fid, s1); - ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); - env_->SetStaticObjectField(c, s_fid, s2); - ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid))); - - env_->SetObjectField(o, i_fid, s1); - ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid))); - env_->SetObjectField(o, i_fid, s2); - ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); +TEST_F(JniInternalTest, GetObjectField_SetObjectField_subclass) { + TEST_OBJECT_FIELD_FOR_CLASS("AllFieldsSub"); } TEST_F(JniInternalTest, NewLocalRef_nullptr) { diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index eb54f7fb1f..b4f5d81067 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -440,7 +440,6 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to, // cache. Use LookupResolveType here to search the class table if it is not in the dex cache. // should be no thread suspension due to the class being resolved. ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); @@ -477,7 +476,6 @@ inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to, // The referenced class has already been resolved with the method, but may not be in the dex // cache. ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index a0a2f46433..e0a341da67 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1035,7 +1035,7 @@ ObjPtr<Class> Class::GetDirectInterface(Thread* self, ObjPtr<Class> klass, uint3 return interfaces->Get(idx); } else { dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - ObjPtr<Class> interface = ClassLinker::LookupResolvedType( + ObjPtr<Class> interface = Runtime::Current()->GetClassLinker()->LookupResolvedType( type_idx, klass->GetDexCache(), klass->GetClassLoader()); return interface; } @@ -1047,9 +1047,7 @@ ObjPtr<Class> Class::ResolveDirectInterface(Thread* self, Handle<Class> klass, u DCHECK(!klass->IsArrayClass()); DCHECK(!klass->IsProxyClass()); dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), - type_idx, - klass.Get()); + interface = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, klass.Get()); CHECK(interface != nullptr || self->IsExceptionPending()); } return interface; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 2d1f886896..1b5c535c87 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -375,8 +375,8 @@ static void PreloadDexCachesResolveField(ObjPtr<mirror::DexCache> dex_cache, } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(field_id.class_idx_, dex_cache, nullptr); + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + field_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } @@ -401,8 +401,8 @@ static void PreloadDexCachesResolveMethod(ObjPtr<mirror::DexCache> dex_cache, ui } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache, nullptr); + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + method_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 9359ffc7fd..da5cee1ddc 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -146,11 +146,11 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { // with kActiveTransaction == false. DCHECK(!Runtime::Current()->IsActiveTransaction()); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); MutableHandle<mirror::Class> interface(hs.NewHandle<mirror::Class>(nullptr)); for (uint32_t i = 0; i < num_ifaces; ++i) { const dex::TypeIndex type_idx = iface_list->GetTypeItem(i).type_idx_; - interface.Assign(ClassLinker::LookupResolvedType( - type_idx, klass->GetDexCache(), klass->GetClassLoader())); + interface.Assign(linker->LookupResolvedType(type_idx, klass.Get())); ifaces->SetWithoutChecks<false>(i, interface.Get()); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 7239ad3213..1cdeb7c77c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -297,6 +297,7 @@ Runtime::~Runtime() { } if (dump_gc_performance_on_shutdown_) { + ScopedLogSeverity sls(LogSeverity::INFO); // This can't be called from the Heap destructor below because it // could call RosAlloc::InspectAll() which needs the thread_list // to be still alive. diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 24f83b881d..4ff49edb90 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -230,7 +230,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, previous_method_idx = method_idx; InvokeType type = it->GetMethodInvokeType(class_def); ArtMethod* method = linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - *dex_file, method_idx, dex_cache, class_loader, nullptr, type); + method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); if (method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. @@ -1077,9 +1077,8 @@ bool MethodVerifier::ScanTryCatchBlocks() { // Ensure exception types are resolved so that they don't need resolution to be delivered, // unresolved exception types will be ignored by exception delivery if (iterator.GetHandlerTypeIndex().IsValid()) { - ObjPtr<mirror::Class> exception_type = linker->ResolveType(*dex_file_, - iterator.GetHandlerTypeIndex(), - dex_cache_, class_loader_); + ObjPtr<mirror::Class> exception_type = + linker->ResolveType(iterator.GetHandlerTypeIndex(), dex_cache_, class_loader_); if (exception_type == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -2434,8 +2433,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& res_type = ResolveClass<CheckAccess::kYes>(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. - ObjPtr<mirror::Class> klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get()); + ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache_.Get(), class_loader_.Get()); if (klass != nullptr && klass->IsPrimitive()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type " << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in " @@ -3643,7 +3642,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { // It is also a catch-all if it is java.lang.Throwable. ObjPtr<mirror::Class> klass = - linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_, class_loader_); + linker->ResolveType(handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { if (klass == mirror::Throwable::GetJavaLangThrowable()) { has_catch_all_handler = true; @@ -3767,10 +3766,10 @@ inline bool MethodVerifier::IsInstantiableOrPrimitive(ObjPtr<mirror::Class> klas template <MethodVerifier::CheckAccess C> const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { + ClassLinker* linker = Runtime::Current()->GetClassLinker(); ObjPtr<mirror::Class> klass = can_load_classes_ - ? Runtime::Current()->GetClassLinker()->ResolveType( - *dex_file_, class_idx, dex_cache_, class_loader_) - : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); + ? linker->ResolveType(class_idx, dex_cache_, class_loader_) + : linker->LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); if (can_load_classes_ && klass == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 1d05160883..b09b9a22eb 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -1,4 +1,5 @@ JNI_OnLoad called +ABC.XYZ = 12, GetStaticIntField(DEF.class, 'XYZ') = 12 Super.<init> Super.<init> Subclass.<init> diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index 4561895509..33a8f5bba2 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -90,6 +90,14 @@ static void testFindClassOnAttachedNativeThread(JNIEnv* env) { CHECK(!env->ExceptionCheck()); } +extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldSubclass(JNIEnv* env, + jclass, + jobject f_obj, + jclass sub) { + jfieldID f = env->FromReflectedField(f_obj); + return env->GetStaticIntField(sub, f); +} + // http://b/10994325 extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) { PthreadHelper(&testFindClassOnAttachedNativeThread); diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index 871107c56b..f94dcf6c70 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -18,6 +18,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.regex.Pattern; @@ -32,6 +33,7 @@ public class Main { throw new RuntimeException("Slow-debug flags unexpectedly off."); } + testFieldSubclass(); testFindClassOnAttachedNativeThread(); testFindFieldOnAttachedNativeThread(); testReflectFieldGetFromAttachedNativeThreadNative(); @@ -65,6 +67,19 @@ public class Main { testDoubleLoad(args[0]); } + static class ABC { public static int XYZ = 12; } + static class DEF extends ABC {} + public static void testFieldSubclass() { + try { + System.out.println("ABC.XYZ = " + ABC.XYZ + ", GetStaticIntField(DEF.class, 'XYZ') = " + + getFieldSubclass(ABC.class.getDeclaredField("XYZ"), DEF.class)); + } catch (Exception e) { + throw new RuntimeException("Failed to test get static field on a subclass", e); + } + } + + public static native int getFieldSubclass(Field f, Class sub); + private static native boolean registerNativesJniTest(); private static native void testCallDefaultMethods(); diff --git a/test/137-cfi/run b/test/137-cfi/run index ebc729bc74..adea71a07f 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -16,10 +16,15 @@ # Test with full DWARF debugging information. # Check full signatures of methods. +# The option jitthreshold:0 ensures that if we run the test in JIT mode, +# there will be JITed frames on the callstack (it synchronously JITs on first use). ${RUN} "$@" -Xcompiler-option --generate-debug-info \ + --runtime-option -Xjitthreshold:0 \ --args --full-signatures --args --test-local --args --test-remote # Test with minimal compressed debugging information. # Check only method names (parameters are omitted to save space). # Check only remote unwinding since decompression is disabled in local unwinds (b/27391690). -${RUN} "$@" -Xcompiler-option --generate-mini-debug-info --args --test-remote +${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \ + --runtime-option -Xjitthreshold:0 \ + --args --test-remote diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check new file mode 100755 index 0000000000..d2c03841fc --- /dev/null +++ b/test/1940-ddms-ext/check @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Need to pull out the describeException ouput since that won't be there on +# device. +sed -e '/\t.*$/d' "$2" | sed -e '/java.lang.ArrayIndexOutOfBoundsException:.*$/d' > "$2.tmp" + +./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt index 62d3b7bd4c..1a457a01a5 100644 --- a/test/1940-ddms-ext/expected.txt +++ b/test/1940-ddms-ext/expected.txt @@ -3,8 +3,19 @@ MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 8, data: [1, 2, 3, 4, MyDdmHandler: Putting value 0x800025 MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +Sending empty data array +MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 0, data: []) +MyDdmHandler: Putting value 0x1 +MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, 0, 0, 1]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, 0, 0, 1]) Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Sending data [1] to chunk handler -1412567295 +MyDdmHandler: Chunk received: Chunk(Type: 0xABCDEF01, Len: 1, data: [1]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 0, data: []) +Sending data [1] to chunk handler 305419896 +MyDdmHandler: Chunk received: Chunk(Type: 0x12345678, Len: 1, data: [1]) +Got error: JVMTI_ERROR_INTERNAL Saw expected thread events. Expected chunk type published: 1213221190 Expected chunk type published: 1297109829 diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java index 9f79eaebba..226fe350bd 100644 --- a/test/1940-ddms-ext/src-art/art/Test1940.java +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -30,6 +30,8 @@ public class Test1940 { public static final int DDMS_HEADER_LENGTH = 8; public static final int MY_DDMS_TYPE = 0xDEADBEEF; public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + public static final int MY_EMPTY_DDMS_TYPE = 0xABCDEF01; + public static final int MY_INVALID_DDMS_TYPE = 0x12345678; public static final boolean PRINT_ALL_CHUNKS = false; @@ -58,19 +60,27 @@ public class Test1940 { public void connected() {} public void disconnected() {} public Chunk handleChunk(Chunk req) { - // For this test we will simply calculate the checksum - checkEq(req.type, MY_DDMS_TYPE); System.out.println("MyDdmHandler: Chunk received: " + printChunk(req)); - ByteBuffer b = ByteBuffer.wrap(new byte[8]); - Adler32 a = new Adler32(); - a.update(req.data, req.offset, req.length); - b.order(ByteOrder.BIG_ENDIAN); - long val = a.getValue(); - b.putLong(val); - System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); - Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); - System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); - return ret; + if (req.type == MY_DDMS_TYPE) { + // For this test we will simply calculate the checksum + ByteBuffer b = ByteBuffer.wrap(new byte[8]); + Adler32 a = new Adler32(); + a.update(req.data, req.offset, req.length); + b.order(ByteOrder.BIG_ENDIAN); + long val = a.getValue(); + b.putLong(val); + System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); + Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); + System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); + return ret; + } else if (req.type == MY_EMPTY_DDMS_TYPE) { + return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[0], 0, 0); + } else if (req.type == MY_INVALID_DDMS_TYPE) { + // This is a very invalid chunk. + return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[] { 0 }, /*offset*/ 12, /*length*/ 55); + } else { + throw new TestError("Unknown ddm request type: " + req.type); + } } } @@ -113,18 +123,42 @@ public class Test1940 { Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); // Test sending chunk directly. DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registerHandler(MY_EMPTY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registerHandler(MY_INVALID_DDMS_TYPE, SINGLE_HANDLER); DdmServer.registrationComplete(); byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; System.out.println("Sending data " + Arrays.toString(data)); Chunk res = processChunk(data); System.out.println("JVMTI returned chunk: " + printChunk(res)); + // Test sending an empty chunk. + System.out.println("Sending empty data array"); + res = processChunk(new byte[0]); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + // Test sending chunk through DdmServer#sendChunk Chunk c = new Chunk( MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); System.out.println("Sending chunk: " + printChunk(c)); DdmServer.sendChunk(c); + // Test getting back an empty chunk. + data = new byte[] { 0x1 }; + System.out.println( + "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_EMPTY_DDMS_TYPE); + res = processChunk(new Chunk(MY_EMPTY_DDMS_TYPE, data, 0, 1)); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + + // Test getting back an invalid chunk. + System.out.println( + "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_INVALID_DDMS_TYPE); + try { + res = processChunk(new Chunk(MY_INVALID_DDMS_TYPE, data, 0, 1)); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + } catch (RuntimeException e) { + System.out.println("Got error: " + e.getMessage()); + } + // Test thread chunks are sent. final boolean[] types_seen = new boolean[] { false, false, false }; CURRENT_HANDLER = (type, cdata) -> { diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java index 7797f31867..ccaba61722 100644 --- a/test/458-checker-instruct-simplification/src/Main.java +++ b/test/458-checker-instruct-simplification/src/Main.java @@ -2602,6 +2602,124 @@ public class Main { return (byte)((int)(((long)(b & 0xff)) & 255L)); } + /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) instruction_simplifier (before) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Cond0:z\d+>> Equal [<<Param>>,<<Const0>>] + /// CHECK-DAG: If [<<Cond0>>] + /// CHECK-DAG: <<Cond1:z\d+>> Equal [<<Param>>,<<Const0>>] + /// CHECK-DAG: If [<<Cond1>>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const0>>] + /// CHECK-DAG: <<Cond2:z\d+>> Equal [<<Param>>,<<Const0>>] + /// CHECK-DAG: If [<<Cond2>>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) instruction_simplifier (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: If [<<Param>>] + /// CHECK-DAG: <<Cond1:z\d+>> Equal [<<Const1>>,<<Const0>>] + /// CHECK-DAG: If [<<Cond1>>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] + /// CHECK-DAG: <<Cond2:z\d+>> Equal [<<Const0>>,<<Const0>>] + /// CHECK-DAG: If [<<Cond2>>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const0>>] + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testIfCondStaticEvaluation(int[], boolean) dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: If [<<Param>>] + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] + // + /// CHECK-NOT: IntConstant 0 + /// CHECK-NOT: If [<<Param>>] + /// CHECK-NOT: ArraySet + private static void $noinline$testIfCondStaticEvaluation(int[] a, boolean f) { + if (f) { + if (f) { + a[0] = 1; + } + } else { + if (f) { + a[0] = 0; + } + } + } + + /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) instruction_simplifier (before) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Cond0:z\d+>> Equal [<<Param>>,<<Const0>>] loop:none + /// CHECK-DAG: If [<<Cond0>>] loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<LoopCheck:z\d+>> GreaterThanOrEqual loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<LoopCheck>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Cond1:z\d+>> NotEqual [<<Param>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Cond1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) instruction_simplifier (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: If [<<Param>>] loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<LoopCheck:z\d+>> GreaterThanOrEqual loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<LoopCheck>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Cond1:z\d+>> NotEqual [<<Const0>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Cond1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + + /// CHECK-START: void Main.$noinline$testManualUnrollWithInvarExits(int[], boolean) dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: If [<<Param>>] loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] loop:none + /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<LoopCheck:z\d+>> GreaterThanOrEqual loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<LoopCheck>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Const1>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: If + /// CHECK-NOT: ArraySet + private static void $noinline$testManualUnrollWithInvarExits(int[] a, boolean f) { + if (f) { + return; + } + a[0] = 1; + for (int i = 1; i < a.length; i++) { + if (f) { + return; + } + a[i] = 1; + } + } + + public static final int LENGTH = 1024; + + private static final void initArray(int[] a) { + for (int i = 0; i < a.length; i++) { + a[i] = 0; + } + } + public static void main(String[] args) { int arg = 123456; float floatArg = 123456.125f; @@ -2845,6 +2963,26 @@ public class Main { assertIntEquals(1, $noinline$bug68142795Boolean(true)); assertIntEquals(0x7f, $noinline$bug68142795Elaborate((byte) 0x7f)); assertIntEquals((byte) 0x80, $noinline$bug68142795Elaborate((byte) 0x80)); + + int[] array = new int[LENGTH]; + + array[0] = 0; + $noinline$testIfCondStaticEvaluation(array, true); + assertIntEquals(array[0], 1); + array[0] = 0; + $noinline$testIfCondStaticEvaluation(array, false); + assertIntEquals(array[0], 0); + + initArray(array); + $noinline$testManualUnrollWithInvarExits(array, false); + for (int i = 0; i < array.length; i++) { + assertIntEquals(array[i], 1); + } + initArray(array); + $noinline$testManualUnrollWithInvarExits(array, true); + for (int i = 0; i < array.length; i++) { + assertIntEquals(array[i], 0); + } } private static boolean $inline$true() { return true; } diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java index b867970eab..794308d6e1 100644 --- a/test/667-jit-jni-stub/src/Main.java +++ b/test/667-jit-jni-stub/src/Main.java @@ -135,13 +135,16 @@ public class Main { int count = 0; while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { // If `call` is true, also exercise the `callThrough()` method to increase hotness. - int limit = call ? 1 << Math.min(count, 12) : 0; + // Ramp-up the number of calls we do up to 1 << 12. + final int rampUpCutOff = 12; + int limit = call ? 1 << Math.min(count, rampUpCutOff) : 0; for (int i = 0; i < limit; ++i) { callThrough(Main.class, "doNothing"); } try { - // Sleep to give a chance for the JIT to compile `hasJit` stub. - Thread.sleep(100); + // Sleep to give a chance for the JIT to compile `callThrough` stub. + // After the ramp-up phase, give the JIT even more time to compile. + Thread.sleep(count >= rampUpCutOff ? 200 : 100); } catch (Exception e) { // Ignore } diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java index d21596d4bc..25e4fad714 100644 --- a/test/706-checker-scheduler/src/Main.java +++ b/test/706-checker-scheduler/src/Main.java @@ -523,7 +523,71 @@ public class Main { return res; } + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static final int ARRAY_SIZE = 32; + + // Check that VecReplicateScalar is not reordered. + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (before) + /// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecReplicateScalar loop:<<Loop>> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (after) + /// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecReplicateScalar loop:<<Loop>> outer_loop:none + private static void testVecReplicateScalar() { + for (int j = 0; j <= 8; j++) { + int[] a = new int[ARRAY_SIZE]; + for (int i = 0; i < a.length; i++) { + a[i] += 1; + } + for (int i = 0; i < a.length; i++) { + expectEquals(1, a[i]); + } + } + } + + // Check that VecSetScalars, VecReduce, VecExtractScalar are not reordered. + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (before) + /// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecSetScalars loop:<<Loop>> outer_loop:none + // + /// CHECK: VecReduce loop:<<Loop>> outer_loop:none + /// CHECK: VecExtractScalar loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (after) + /// CHECK: Phi loop:<<Loop:B\d+>> outer_loop:none + /// CHECK: NewArray loop:<<Loop>> outer_loop:none + /// CHECK: VecSetScalars loop:<<Loop>> outer_loop:none + // + /// CHECK: VecReduce loop:<<Loop>> outer_loop:none + /// CHECK: VecExtractScalar loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<<Loop>> outer_loop:none + private static void testVecSetScalars() { + for (int j = 0; j <= 8; j++) { + int[] a = new int[ARRAY_SIZE]; + int s = 5; + for (int i = 0; i < ARRAY_SIZE; i++) { + s+=a[i]; + } + expectEquals(a[0], 0); + expectEquals(s, 5); + } + } + public static void main(String[] args) { + testVecSetScalars(); + testVecReplicateScalar(); if ((arrayAccess() + intDiv(10)) != -35) { System.out.println("FAIL"); } diff --git a/test/AllFields/AllFields.java b/test/AllFields/AllFields.java index d5eac8fa2e..24f8ba1a0b 100644 --- a/test/AllFields/AllFields.java +++ b/test/AllFields/AllFields.java @@ -14,7 +14,7 @@ * limitations under the License. */ -class AllFields { +public class AllFields { static boolean sZ; static byte sB; static char sC; diff --git a/test/AllFields/AllFieldsSub.java b/test/AllFields/AllFieldsSub.java new file mode 100644 index 0000000000..d5f933f88d --- /dev/null +++ b/test/AllFields/AllFieldsSub.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class AllFieldsSub extends AllFields { } diff --git a/test/AllFields/AllFieldsUnrelated.java b/test/AllFields/AllFieldsUnrelated.java new file mode 100644 index 0000000000..4db66b1886 --- /dev/null +++ b/test/AllFields/AllFieldsUnrelated.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class AllFieldsUnrelated { } diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/external_oj_libjdwp_art_failures.txt index 145022e7eb..1178af4852 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/external_oj_libjdwp_art_failures.txt @@ -1,6 +1,9 @@ /* * This file contains expectations for ART's buildbot. The purpose of this file is * to temporarily list failing tests and not break the bots. + * + * This file contains the expectations for the 'libjdwp-aot' and 'libjdwp-jit' + * test groups on the chromium buildbot. */ [ { @@ -69,12 +72,6 @@ name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" }, { - description: "Test for ddms extensions that are not yet implemented", - result: EXEC_FAILED, - bug: 69169846, - name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" -}, -{ description: "Test crashes", result: EXEC_FAILED, bug: 69591477, @@ -84,6 +81,7 @@ description: "Test times out on fugu-debug", result: EXEC_FAILED, bug: 70459916, - name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug" + names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" ] } ] diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index 4edfe1b35b..bee2396fea 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -50,7 +50,7 @@ How to start JFuzz testing [--report_script=SCRIPT] [--jfuzz_arg=ARG] [--true_divergence] - [--use_dx] + [--dexer=DEXER] where @@ -66,7 +66,7 @@ where --report_script : path to script called for each divergence --jfuzz_arg : argument for jfuzz --true_divergence : don't bisect timeout divergences - --use_dx : use dx (rather than jack) + --dexer=DEXER : use either dx, d8, or jack to obtain dex files How to start JFuzz nightly testing ================================== @@ -87,14 +87,14 @@ How to start J/DexFuzz testing (multi-layered) [--num_tests=NUM_TESTS] [--num_inputs=NUM_INPUTS] [--device=DEVICE] - [--use_dx] + [--dexer=DEXER] where - --num_tests : number of tests to run (10000 by default) - --num_inputs: number of JFuzz programs to generate - --device : target device serial number (passed to adb -s) - --use_dx : use dx (rather than jack) + --num_tests : number of tests to run (10000 by default) + --num_inputs : number of JFuzz programs to generate + --device : target device serial number (passed to adb -s) + --dexer=DEXER : use either dx, d8, or jack to obtain dex files Background ========== diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py index ca0aec01cc..fdff9c03fa 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -41,14 +41,14 @@ from common.common import RunCommand class DexFuzzTester(object): """Tester that feeds JFuzz programs into DexFuzz testing.""" - def __init__(self, num_tests, num_inputs, device, use_dx): + def __init__(self, num_tests, num_inputs, device, dexer): """Constructor for the tester. Args: num_tests: int, number of tests to run num_inputs: int, number of JFuzz programs to generate device: string, target device serial number (or None) - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ self._num_tests = num_tests self._num_inputs = num_inputs @@ -58,7 +58,7 @@ class DexFuzzTester(object): self._dexfuzz_dir = None self._inputs_dir = None self._dexfuzz_env = None - self._use_dx = use_dx + self._dexer = dexer def __enter__(self): """On entry, enters new temp directory after saving current directory. @@ -109,13 +109,14 @@ class DexFuzzTester(object): Raises: FatalError: error when compilation fails """ - if self._use_dx: + if self._dexer == 'dx' or self._dexer == 'd8': if RunCommand(['javac', 'Test.java'], out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running javac') raise FatalError('Unexpected error while running javac') cfiles = glob('*.class') - if RunCommand(['dx', '--dex', '--output=classes.dex'] + cfiles, + dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' + if RunCommand([dx, '--dex', '--output=classes.dex'] + cfiles, out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running dx') raise FatalError('Unexpected error while running dx') @@ -124,7 +125,8 @@ class DexFuzzTester(object): os.unlink(cfile) os.unlink('jerr.txt') os.unlink('dxerr.txt') - else: + + elif self._dexer == 'jack': jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt', timeout=30) != RetCode.SUCCESS: @@ -132,6 +134,8 @@ class DexFuzzTester(object): raise FatalError('Unexpected error while running Jack') # Cleanup on success (nothing to see). os.unlink('jackerr.txt') + else: + raise FatalError('Unknown dexer: ' + self._dexer) def GenerateJFuzzPrograms(self): """Generates JFuzz programs. @@ -175,16 +179,16 @@ class DexFuzzTester(object): def main(): # Handle arguments. parser = argparse.ArgumentParser() - parser.add_argument('--num_tests', default=1000, - type=int, help='number of tests to run') - parser.add_argument('--num_inputs', default=10, - type=int, help='number of JFuzz program to generate') - parser.add_argument('--use_dx', default=False, action='store_true', - help='use dx (rather than jack)') + parser.add_argument('--num_tests', default=1000, type=int, + help='number of tests to run (default: 1000)') + parser.add_argument('--num_inputs', default=10, type=int, + help='number of JFuzz program to generate (default: 10)') + parser.add_argument('--dexer', default='dx', type=str, + help='defines dexer as dx, d8, or jack (default: dx)') parser.add_argument('--device', help='target device serial number') args = parser.parse_args() # Run the DexFuzz tester. - with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.use_dx) as fuzzer: + with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.dexer) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index dac1c79817..b88994013e 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -43,11 +43,11 @@ from common.common import DeviceTestEnv BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT) -def GetExecutionModeRunner(use_dx, device, mode): +def GetExecutionModeRunner(dexer, device, mode): """Returns a runner for the given execution mode. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) mode: string, execution mode Returns: @@ -58,13 +58,13 @@ def GetExecutionModeRunner(use_dx, device, mode): if mode == 'ri': return TestRunnerRIOnHost() if mode == 'hint': - return TestRunnerArtIntOnHost(use_dx) + return TestRunnerArtIntOnHost(dexer) if mode == 'hopt': - return TestRunnerArtOptOnHost(use_dx) + return TestRunnerArtOptOnHost(dexer) if mode == 'tint': - return TestRunnerArtIntOnTarget(use_dx, device) + return TestRunnerArtIntOnTarget(dexer, device) if mode == 'topt': - return TestRunnerArtOptOnTarget(use_dx, device) + return TestRunnerArtOptOnTarget(dexer, device) raise FatalError('Unknown execution mode') @@ -117,27 +117,30 @@ class TestRunner(object): class TestRunnerWithHostCompilation(TestRunner): """Abstract test runner that supports compilation on host.""" - def __init__(self, use_dx): + def __init__(self, dexer): """Constructor for the runner with host compilation. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] - self._use_dx = use_dx + self._dexer = dexer def CompileOnHost(self): - if self._use_dx: + if self._dexer == 'dx' or self._dexer == 'd8': if RunCommand(['javac', 'Test.java'], out=None, err=None, timeout=30) == RetCode.SUCCESS: - retc = RunCommand(['dx', '--dex', '--output=classes.dex'] + glob('*.class'), + dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' + retc = RunCommand([dx, '--dex', '--output=classes.dex'] + glob('*.class'), out=None, err='dxerr.txt', timeout=30) else: retc = RetCode.NOTCOMPILED - else: + elif self._dexer == 'jack': retc = RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt', timeout=30) + else: + raise FatalError('Unknown dexer: ' + self._dexer) return retc @@ -167,14 +170,14 @@ class TestRunnerRIOnHost(TestRunner): class TestRunnerArtOnHost(TestRunnerWithHostCompilation): """Abstract test runner of Art on host.""" - def __init__(self, use_dx, extra_args=None): + def __init__(self, dexer, extra_args=None): """Constructor for the Art on host tester. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(use_dx) + super().__init__(dexer) self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex'] if extra_args is not None: self._art_cmd += extra_args @@ -191,13 +194,13 @@ class TestRunnerArtOnHost(TestRunnerWithHostCompilation): class TestRunnerArtIntOnHost(TestRunnerArtOnHost): """Concrete test runner of interpreter mode Art on host.""" - def __init__(self, use_dx): + def __init__(self, dexer): """Constructor for the Art on host tester (interpreter). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ - super().__init__(use_dx, ['-Xint']) + super().__init__(dexer, ['-Xint']) @property def description(self): @@ -214,13 +217,13 @@ class TestRunnerArtIntOnHost(TestRunnerArtOnHost): class TestRunnerArtOptOnHost(TestRunnerArtOnHost): """Concrete test runner of optimizing compiler mode Art on host.""" - def __init__(self, use_dx): + def __init__(self, dexer): """Constructor for the Art on host tester (optimizing). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ - super().__init__(use_dx, None) + super().__init__(dexer, None) @property def description(self): @@ -239,15 +242,15 @@ class TestRunnerArtOptOnHost(TestRunnerArtOnHost): class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): """Abstract test runner of Art on target.""" - def __init__(self, use_dx, device, extra_args=None): + def __init__(self, dexer, device, extra_args=None): """Constructor for the Art on target tester. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(use_dx) + super().__init__(dexer) self._test_env = DeviceTestEnv('jfuzz_', specific_device=device) self._dalvik_cmd = ['dalvikvm'] if extra_args is not None: @@ -281,14 +284,14 @@ class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): """Concrete test runner of interpreter mode Art on target.""" - def __init__(self, use_dx, device): + def __init__(self, dexer, device): """Constructor for the Art on target tester (interpreter). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) """ - super().__init__(use_dx, device, ['-Xint']) + super().__init__(dexer, device, ['-Xint']) @property def description(self): @@ -305,14 +308,14 @@ class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget): """Concrete test runner of optimizing compiler mode Art on target.""" - def __init__(self, use_dx, device): + def __init__(self, dexer, device): """Constructor for the Art on target tester (optimizing). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer device: string, target device serial number (or None) """ - super().__init__(use_dx, device, None) + super().__init__(dexer, device, None) @property def description(self): @@ -342,7 +345,7 @@ class JFuzzTester(object): """Tester that runs JFuzz many times and report divergences.""" def __init__(self, num_tests, device, mode1, mode2, jfuzz_args, - report_script, true_divergence_only, use_dx): + report_script, true_divergence_only, dexer): """Constructor for the tester. Args: @@ -353,16 +356,16 @@ class JFuzzTester(object): jfuzz_args: list of strings, additional arguments for jfuzz report_script: string, path to script called for each divergence true_divergence_only: boolean, if True don't bisect timeout divergences - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer """ self._num_tests = num_tests self._device = device - self._runner1 = GetExecutionModeRunner(use_dx, device, mode1) - self._runner2 = GetExecutionModeRunner(use_dx, device, mode2) + self._runner1 = GetExecutionModeRunner(dexer, device, mode1) + self._runner2 = GetExecutionModeRunner(dexer, device, mode2) self._jfuzz_args = jfuzz_args self._report_script = report_script self._true_divergence_only = true_divergence_only - self._use_dx = use_dx + self._dexer = dexer self._save_dir = None self._results_dir = None self._jfuzz_dir = None @@ -405,7 +408,7 @@ class JFuzzTester(object): print('Directory :', self._results_dir) print('Exec-mode1:', self._runner1.description) print('Exec-mode2:', self._runner2.description) - print('Compiler :', 'dx' if self._use_dx else 'jack') + print('Dexer :', self._dexer) print() self.ShowStats() for self._test in range(1, self._num_tests + 1): @@ -525,8 +528,7 @@ class JFuzzTester(object): for arg in jfuzz_cmd_str.strip().split(' -')][1:] wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args] repro_cmd_str = (os.path.basename(__file__) + - ' --num_tests=1 ' + - ('--use_dx ' if self._use_dx else '') + + ' --num_tests=1 --dexer=' + self._dexer + ' '.join(wrapped_args)) comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format( jfuzz_ver, jfuzz_cmd_str, repro_cmd_str) @@ -592,21 +594,22 @@ class JFuzzTester(object): def main(): # Handle arguments. parser = argparse.ArgumentParser() - parser.add_argument('--num_tests', default=10000, - type=int, help='number of tests to run') + parser.add_argument('--num_tests', default=10000, type=int, + help='number of tests to run') parser.add_argument('--device', help='target device serial number') parser.add_argument('--mode1', default='ri', help='execution mode 1 (default: ri)') parser.add_argument('--mode2', default='hopt', help='execution mode 2 (default: hopt)') - parser.add_argument('--report_script', help='script called for each' - ' divergence') + parser.add_argument('--report_script', + help='script called for each divergence') parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args', - action='append', help='argument for jfuzz') + action='append', + help='argument for jfuzz') parser.add_argument('--true_divergence', default=False, action='store_true', - help='don\'t bisect timeout divergences') - parser.add_argument('--use_dx', default=False, action='store_true', - help='use dx (rather than jack)') + help='do not bisect timeout divergences') + parser.add_argument('--dexer', default='dx', type=str, + help='defines dexer as dx, d8, or jack (default: dx)') args = parser.parse_args() if args.mode1 == args.mode2: raise FatalError('Identical execution modes given') @@ -614,7 +617,7 @@ def main(): with JFuzzTester(args.num_tests, args.device, args.mode1, args.mode2, args.jfuzz_args, args.report_script, - args.true_divergence, args.use_dx) as fuzzer: + args.true_divergence, args.dexer) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt index bf1c9370b0..7694a4c7e4 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/prebuilt_libjdwp_art_failures.txt @@ -1,6 +1,9 @@ /* * This file contains expectations for ART's buildbot. The purpose of this file is * to temporarily list failing tests and not break the bots. + * + * This file contains the expectations for the 'prebuilt-libjdwp-aot' and + * 'prebuilt-libjdwp-jit' test groups on the chromium buildbot. */ [ { @@ -67,7 +70,8 @@ { description: "Tests for VMDebug functionality not implemented in the upstream libjdwp", result: EXEC_FAILED, - name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug" + names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" ] }, /* TODO Categorize these failures more. */ { diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh index 47e7c4595d..e116facd98 100755 --- a/tools/run-libjdwp-tests.sh +++ b/tools/run-libjdwp-tests.sh @@ -79,7 +79,7 @@ else args+=(-Xplugin:libopenjdkjvmti.so) fi -expect_path=$PWD/art/tools/libjdwp_oj_art_failures.txt +expect_path=$PWD/art/tools/external_oj_libjdwp_art_failures.txt function verbose_run() { echo "$@" env "$@" diff --git a/tools/run-prebuilt-libjdwp-tests.sh b/tools/run-prebuilt-libjdwp-tests.sh index 46c2a153a7..e7f028ae63 100755 --- a/tools/run-prebuilt-libjdwp-tests.sh +++ b/tools/run-prebuilt-libjdwp-tests.sh @@ -96,7 +96,7 @@ if [[ ! -f $plugin ]]; then fi props_path=$PWD/art/tools/libjdwp-compat.props -expect_path=$PWD/art/tools/libjdwp_art_failures.txt +expect_path=$PWD/art/tools/prebuilt_libjdwp_art_failures.txt function verbose_run() { echo "$@" |