diff options
author | 2024-03-07 17:45:56 +0000 | |
---|---|---|
committer | 2024-03-08 11:22:13 +0000 | |
commit | a6f0274f4190c196736993ce6ad9138e687df569 (patch) | |
tree | b7c608052b56049530f0c6967d0d1053c34bac65 | |
parent | 00464160ac51fb1ece69fe883fcaefb484a3932a (diff) |
Revert "Reuse boot JNI stub for native methods"
This reverts commit c8b6e26aa56bb6761bb781d1095b36f84c4c65d4.
Bug: 288983053
Reason for revert: Failure on luci bots.
Change-Id: Ieae0f47f2114efbfcb0bc4d8ffb0868001c22653
-rw-r--r-- | compiler/common_compiler_test.h | 2 | ||||
-rw-r--r-- | dex2oat/driver/compiler_driver.cc | 16 | ||||
-rw-r--r-- | dex2oat/linker/image_writer.cc | 68 | ||||
-rw-r--r-- | dex2oat/linker/image_writer.h | 16 | ||||
-rw-r--r-- | libartbase/base/hash_map.h | 7 | ||||
-rw-r--r-- | runtime/Android.bp | 2 | ||||
-rw-r--r-- | runtime/class_linker.cc | 38 | ||||
-rw-r--r-- | runtime/class_linker.h | 16 | ||||
-rw-r--r-- | runtime/gc/space/image_space.cc | 3 | ||||
-rw-r--r-- | runtime/oat/image-inl.h | 30 | ||||
-rw-r--r-- | runtime/oat/image.cc | 7 | ||||
-rw-r--r-- | runtime/oat/image.h | 12 | ||||
-rw-r--r-- | runtime/oat/jni_stub_hash_map.cc | 355 | ||||
-rw-r--r-- | runtime/oat/jni_stub_hash_map.h | 112 | ||||
-rw-r--r-- | runtime/oat/jni_stub_hash_map_test.cc | 304 | ||||
-rw-r--r-- | runtime/runtime_image.cc | 11 | ||||
-rw-r--r-- | runtime/stack.cc | 23 | ||||
-rw-r--r-- | test/667-jit-jni-stub/src/Main.java | 9 | ||||
-rw-r--r-- | test/MyClassNatives/MyClassNatives.java | 119 | ||||
-rw-r--r-- | test/common/runtime_state.cc | 16 |
20 files changed, 22 insertions, 1144 deletions
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 89f3fb40ad..9b49c93ea6 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -89,10 +89,10 @@ class EXPORT CommonCompilerTestImpl { protected: virtual ClassLinker* GetClassLinker() = 0; virtual Runtime* GetRuntime() = 0; - class OneCompiledMethodStorage; private: class CodeAndMetadata; + class OneCompiledMethodStorage; std::vector<CodeAndMetadata> code_and_metadata_; }; diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc index 39a68537e2..824319b299 100644 --- a/dex2oat/driver/compiler_driver.cc +++ b/dex2oat/driver/compiler_driver.cc @@ -488,18 +488,10 @@ static void CompileMethodQuick( // Query any JNI optimization annotations such as @FastNative or @CriticalNative. access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( dex_file, dex_file.GetClassDef(class_def_idx), method_idx); - const void* boot_jni_stub = nullptr; - if (!Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()) { - // Skip the compilation for native method if found an usable boot JNI stub. - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - std::string_view shorty = dex_file.GetMethodShortyView(dex_file.GetMethodId(method_idx)); - boot_jni_stub = class_linker->FindBootJniStub(access_flags, shorty); - } - if (boot_jni_stub == nullptr) { - compiled_method = - driver->GetCompiler()->JniCompile(access_flags, method_idx, dex_file, dex_cache); - CHECK(compiled_method != nullptr); - } + + compiled_method = driver->GetCompiler()->JniCompile( + access_flags, method_idx, dex_file, dex_cache); + CHECK(compiled_method != nullptr); } } else if ((access_flags & kAccAbstract) != 0) { // Abstract methods don't have code. diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index d5ab5d3345..b663d32e2e 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -571,7 +571,6 @@ bool ImageWriter::Write(int image_fd, for (size_t i = 0; i < oat_filenames_.size(); ++i) { CreateHeader(i, component_count); CopyAndFixupNativeData(i); - CopyAndFixupJniStubMethods(i); } } @@ -1464,14 +1463,6 @@ void ImageWriter::RecordNativeRelocations(ObjPtr<mirror::Class> klass, size_t oa for (auto& m : klass->GetMethods(target_ptr_size_)) { AssignMethodOffset(&m, type, oat_index); } - // Only write JNI stub methods in boot images, but not in boot image extensions and app images. - if (compiler_options_.IsBootImage() && compiler_options_.IsJniCompilationEnabled()) { - for (auto& m : klass->GetMethods(target_ptr_size_)) { - if (m.IsNative() && !m.IsIntrinsic()) { - AssignJniStubMethodOffset(&m, oat_index); - } - } - } (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; } // Assign offsets for all runtime methods in the IMT since these may hold conflict tables @@ -1553,20 +1544,6 @@ void ImageWriter::AssignMethodOffset(ArtMethod* method, image_info.IncrementBinSlotSize(bin_type, ArtMethod::Size(target_ptr_size_)); } -void ImageWriter::AssignJniStubMethodOffset(ArtMethod* method, size_t oat_index) { - CHECK(method->IsNative()); - auto it = jni_stub_map_.find(JniStubKey(method)); - if (it == jni_stub_map_.end()) { - ImageInfo& image_info = GetImageInfo(oat_index); - constexpr Bin bin_type = Bin::kJniStubMethod; - size_t offset = image_info.GetBinSlotSize(bin_type); - jni_stub_map_.Put(std::make_pair( - JniStubKey(method), - std::make_pair(method, JniStubMethodRelocation{oat_index, offset}))); - image_info.IncrementBinSlotSize(bin_type, static_cast<size_t>(target_ptr_size_)); - } -} - class ImageWriter::LayoutHelper { public: explicit LayoutHelper(ImageWriter* image_writer) @@ -2630,14 +2607,6 @@ void ImageWriter::CalculateNewObjectOffsets() { ImageInfo& image_info = GetImageInfo(relocation.oat_index); relocation.offset += image_info.GetBinSlotOffset(bin_type); } - - // Update the JNI stub methods by adding their bin sums. - for (auto& pair : jni_stub_map_) { - JniStubMethodRelocation& relocation = pair.second.second; - constexpr Bin bin_type = Bin::kJniStubMethod; - ImageInfo& image_info = GetImageInfo(relocation.oat_index); - relocation.offset += image_info.GetBinSlotOffset(bin_type); - } } std::pair<size_t, dchecked_vector<ImageSection>> @@ -2686,17 +2655,11 @@ ImageWriter::ImageInfo::CreateImageSections() const { ImageSection(GetBinSlotOffset(Bin::kRuntimeMethod), GetBinSlotSize(Bin::kRuntimeMethod)); /* - * JNI Stub Methods section - */ - sections[ImageHeader::kSectionJniStubMethods] = - ImageSection(GetBinSlotOffset(Bin::kJniStubMethod), GetBinSlotSize(Bin::kJniStubMethod)); - - /* * Interned Strings section */ // Round up to the alignment the string table expects. See HashSet::WriteToMemory. - size_t cur_pos = RoundUp(sections[ImageHeader::kSectionJniStubMethods].End(), sizeof(uint64_t)); + size_t cur_pos = RoundUp(sections[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t)); const ImageSection& interned_strings_section = sections[ImageHeader::kSectionInternedStrings] = @@ -3067,21 +3030,6 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { } } -void ImageWriter::CopyAndFixupJniStubMethods(size_t oat_index) { - const ImageInfo& image_info = GetImageInfo(oat_index); - // Copy method's address to JNI stub methods section. - for (auto& pair : jni_stub_map_) { - JniStubMethodRelocation& relocation = pair.second.second; - // Only work with JNI stubs that are in the current oat file. - if (relocation.oat_index != oat_index) { - continue; - } - void** address = reinterpret_cast<void**>(image_info.image_.Begin() + relocation.offset); - ArtMethod* method = pair.second.first; - CopyAndFixupPointer(address, method); - } -} - void ImageWriter::CopyAndFixupMethodPointerArray(mirror::PointerArray* arr) { // Pointer arrays are processed early and each is visited just once. // Therefore we know that this array has not been copied yet. @@ -3566,18 +3514,6 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, // JNI entrypoint: if (orig->IsNative()) { - // Find boot JNI stub for those methods that skipped AOT compilation and don't need - // clinit check. - bool still_needs_clinit_check = orig->StillNeedsClinitCheck<kWithoutReadBarrier>(); - if (!still_needs_clinit_check && - !compiler_options_.IsBootImage() && - quick_code == GetOatAddress(StubType::kQuickGenericJNITrampoline)) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const void* boot_jni_stub = class_linker->FindBootJniStub(orig); - if (boot_jni_stub != nullptr) { - quick_code = boot_jni_stub; - } - } // The native method's pointer is set to a stub to lookup via dlsym. // Note this is not the code_ pointer, that is handled above. StubType stub_type = orig->IsCriticalNative() ? StubType::kJNIDlsymLookupCriticalTrampoline @@ -3748,8 +3684,6 @@ ImageWriter::ImageWriter(const CompilerOptions& compiler_options, image_objects_offset_begin_(0), target_ptr_size_(InstructionSetPointerSize(compiler_options.GetInstructionSet())), image_infos_(oat_filenames.size()), - jni_stub_map_(JniStubKeyHash(compiler_options.GetInstructionSet()), - JniStubKeyEquals(compiler_options.GetInstructionSet())), dirty_methods_(0u), clean_methods_(0u), app_class_loader_(class_loader), diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 20509a9ed1..9dd6c7850e 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -45,7 +45,6 @@ #include "lock_word.h" #include "mirror/dex_cache.h" #include "oat/image.h" -#include "oat/jni_stub_hash_map.h" #include "oat/oat.h" #include "oat/oat_file.h" #include "obj_ptr.h" @@ -210,8 +209,6 @@ class ImageWriter final { kIMTConflictTable, // Runtime methods (always clean, do not have a length prefix array). kRuntimeMethod, - // Methods with unique JNI stubs. - kJniStubMethod, // Metadata bin for data that is temporary during image lifetime. kMetadata, kLast = kMetadata, @@ -453,7 +450,6 @@ class ImageWriter final { // Creates the contiguous image in memory and adjusts pointers. void CopyAndFixupNativeData(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); - void CopyAndFixupJniStubMethods(size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); void CopyAndFixupObjects() REQUIRES_SHARED(Locks::mutator_lock_); void CopyAndFixupObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); template <bool kCheckIfDone> @@ -499,10 +495,6 @@ class ImageWriter final { size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); - // Assign the offset for a method with unique JNI stub. - void AssignJniStubMethodOffset(ArtMethod* method, size_t oat_index) - REQUIRES_SHARED(Locks::mutator_lock_); - // Return true if imt was newly inserted. bool TryAssignImTableOffset(ImTable* imt, size_t oat_index) REQUIRES_SHARED(Locks::mutator_lock_); @@ -538,11 +530,6 @@ class ImageWriter final { NativeObjectRelocationType type; }; - struct JniStubMethodRelocation { - size_t oat_index; - uintptr_t offset; - }; - NativeObjectRelocation GetNativeRelocation(void* obj) REQUIRES_SHARED(Locks::mutator_lock_); // Location of where the object will be when the image is loaded at runtime. @@ -658,9 +645,6 @@ class ImageWriter final { // image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields. HashMap<void*, NativeObjectRelocation> native_object_relocations_; - // HashMap used for generating JniStubMethodsSection. - JniStubHashMap<std::pair<ArtMethod*, JniStubMethodRelocation>> jni_stub_map_; - // Runtime ArtMethods which aren't reachable from any Class but need to be copied into the image. ArtMethod* image_methods_[ImageHeader::kImageMethodsCount]; diff --git a/libartbase/base/hash_map.h b/libartbase/base/hash_map.h index 07245e5816..8823c8bb4d 100644 --- a/libartbase/base/hash_map.h +++ b/libartbase/base/hash_map.h @@ -26,8 +26,6 @@ namespace art { template <typename Key, typename Value, typename HashFn> class HashMapHashWrapper { public: - HashMapHashWrapper() : hash_fn_(HashFn()) {} - explicit HashMapHashWrapper(const HashFn& hashfn) : hash_fn_(hashfn) {} size_t operator()(const Key& key) const { return hash_fn_(key); } @@ -43,8 +41,6 @@ class HashMapHashWrapper { template <typename Key, typename Value, typename PredFn> class HashMapPredWrapper { public: - HashMapPredWrapper() : pred_fn_(PredFn()) {} - explicit HashMapPredWrapper(const PredFn& predfn) : pred_fn_(predfn) {} bool operator()(const std::pair<Key, Value>& a, const std::pair<Key, Value>& b) const { return pred_fn_(a.first, b.first); } @@ -90,9 +86,6 @@ class HashMap : public HashSet<std::pair<Key, Value>, public: // Inherit constructors. using Base::Base; - HashMap(HashFn hashfn, Pred pred) - : Base(HashMapHashWrapper<Key, Value, HashFn>(hashfn), - HashMapPredWrapper<Key, Value, Pred>(pred)) {} // Used to insert a new mapping. typename Base::iterator Overwrite(const Key& k, const Value& v) { diff --git a/runtime/Android.bp b/runtime/Android.bp index 72879e8929..17f09cf814 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -382,7 +382,6 @@ cc_defaults { "oat/elf_file.cc", "oat/image.cc", "oat/index_bss_mapping.cc", - "oat/jni_stub_hash_map.cc", "oat/oat.cc", "oat/oat_file.cc", "oat/oat_file_assistant.cc", @@ -1078,7 +1077,6 @@ art_cc_defaults { "monitor_pool_test.cc", "monitor_test.cc", "native_stack_dump_test.cc", - "oat/jni_stub_hash_map_test.cc", "oat/oat_file_assistant_test.cc", "oat/oat_file_test.cc", "parsed_options_test.cc", diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5a94f57bb6..57dbd6d259 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -408,15 +408,6 @@ void ClassLinker::ForceClassInitialized(Thread* self, Handle<mirror::Class> klas MakeInitializedClassesVisiblyInitialized(self, /*wait=*/true); } -const void* ClassLinker::FindBootJniStub(JniStubKey key) { - auto it = boot_image_jni_stubs_.find(key); - if (it == boot_image_jni_stubs_.end()) { - return nullptr; - } else { - return it->second; - } -} - ClassLinker::VisiblyInitializedCallback* ClassLinker::MarkClassInitialized( Thread* self, Handle<mirror::Class> klass) { if (kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64) { @@ -625,8 +616,6 @@ ClassLinker::ClassLinker(InternTable* intern_table, bool fast_class_not_found_ex visibly_initialize_classes_with_membarier_(RegisterMemBarrierForClassInitialization()), critical_native_code_with_clinit_check_lock_("critical native code with clinit check lock"), critical_native_code_with_clinit_check_(), - boot_image_jni_stubs_(JniStubKeyHash(Runtime::Current()->GetInstructionSet()), - JniStubKeyEquals(Runtime::Current()->GetInstructionSet())), cha_(Runtime::Current()->IsAotCompiler() ? nullptr : new ClassHierarchyAnalysis()) { // For CHA disabled during Aot, see b/34193647. @@ -1448,16 +1437,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { error_msg)) { return false; } - for (gc::space::ImageSpace* space : spaces) { - const ImageHeader& header = space->GetImageHeader(); - header.VisitJniStubMethods([&](ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_) { - const void* stub = method->GetOatMethodQuickCode(image_pointer_size_); - boot_image_jni_stubs_.Put(std::make_pair(JniStubKey(method), stub)); - return method; - }, space->Begin(), image_pointer_size_); - } - InitializeObjectVirtualMethodHashes(GetClassRoot<mirror::Object>(this), image_pointer_size_, ArrayRef<uint32_t>(object_virtual_method_hashes_)); @@ -3699,15 +3678,7 @@ void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> kla for (size_t method_index = 0; method_index < num_direct_methods; ++method_index) { ArtMethod* method = klass->GetDirectMethod(method_index, pointer_size); if (method->NeedsClinitCheckBeforeCall()) { - const void* quick_code = instrumentation->GetCodeForInvoke(method); - if (method->IsNative() && IsQuickGenericJniStub(quick_code)) { - const void* boot_jni_stub = FindBootJniStub(method); - if (boot_jni_stub != nullptr) { - // Use boot JNI stub if found. - quick_code = boot_jni_stub; - } - } - instrumentation->UpdateMethodsCode(method, quick_code); + instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method)); } } // Ignore virtual methods on the iterator. @@ -3751,13 +3722,6 @@ static void LinkCode(ClassLinker* class_linker, const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index); quick_code = oat_method.GetQuickCode(); } - if (method->IsNative() && quick_code == nullptr) { - const void* boot_jni_stub = class_linker->FindBootJniStub(method); - if (boot_jni_stub != nullptr) { - // Use boot JNI stub if found. - quick_code = boot_jni_stub; - } - } runtime->GetInstrumentation()->InitializeMethodsCode(method, quick_code); if (method->IsNative()) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 73b3153c45..5597149178 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -41,7 +41,6 @@ #include "jni.h" #include "mirror/class.h" #include "mirror/object.h" -#include "oat/jni_stub_hash_map.h" #include "oat/oat_file.h" #include "verifier/verifier_enums.h" @@ -889,17 +888,6 @@ class EXPORT ClassLinker { ClassTable* GetBootClassTable() REQUIRES_SHARED(Locks::classlinker_classes_lock_) { return boot_class_table_.get(); } - // Find a matching JNI stub from boot images that we could reuse as entrypoint. - const void* FindBootJniStub(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_) { - return FindBootJniStub(JniStubKey(method)); - } - - const void* FindBootJniStub(uint32_t flags, std::string_view shorty) { - return FindBootJniStub(JniStubKey(flags, shorty)); - } - - const void* FindBootJniStub(JniStubKey key); protected: virtual bool InitializeClass(Thread* self, @@ -1422,10 +1410,6 @@ class EXPORT ClassLinker { std::map<ArtMethod*, void*> critical_native_code_with_clinit_check_ GUARDED_BY(critical_native_code_with_clinit_check_lock_); - // Load unique JNI stubs from boot images. If the subsequently loaded native methods could find a - // matching stub, then reuse it without JIT/AOT compilation. - JniStubHashMap<const void*> boot_image_jni_stubs_; - std::unique_ptr<ClassHierarchyAnalysis> cha_; class FindVirtualMethodHolderVisitor; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 6b1aef9721..e3e14cae76 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -2615,9 +2615,6 @@ class ImageSpace::BootImageLoader { }; image_header.VisitPackedImTables(method_table_visitor, space->Begin(), kPointerSize); image_header.VisitPackedImtConflictTables(method_table_visitor, space->Begin(), kPointerSize); - image_header.VisitJniStubMethods</*kUpdate=*/ true>(method_table_visitor, - space->Begin(), - kPointerSize); // Patch the intern table. if (image_header.GetInternedStringsSection().Size() != 0u) { diff --git a/runtime/oat/image-inl.h b/runtime/oat/image-inl.h index b87bcb2313..5995600c27 100644 --- a/runtime/oat/image-inl.h +++ b/runtime/oat/image-inl.h @@ -115,36 +115,6 @@ inline void ImageHeader::VisitPackedImtConflictTables(const Visitor& visitor, } } -template <bool kUpdate, typename Visitor> -inline void ImageHeader::VisitJniStubMethods(const Visitor& visitor, - uint8_t* base, - PointerSize pointer_size) - const REQUIRES_SHARED(Locks::mutator_lock_) { - const ImageSection& section = GetJniStubMethodsSection(); - uint8_t* updated_base = base + section.Offset(); - for (size_t pos = 0; pos < section.Size(); pos += static_cast<size_t>(pointer_size)) { - uint8_t* ptr = updated_base + pos; - ArtMethod* orig; - if (pointer_size == PointerSize::k32) { - uint32_t value = *reinterpret_cast<uint32_t*>(ptr); - orig = reinterpret_cast32<ArtMethod*>(value); - } else { - uint64_t value = *reinterpret_cast<uint64_t*>(ptr); - orig = reinterpret_cast64<ArtMethod*>(value); - } - ArtMethod* updated = visitor(orig); - if (kUpdate){ - if (pointer_size == PointerSize::k32) { - *reinterpret_cast<uint32_t*>(ptr) = reinterpret_cast32<uint32_t>(updated); - } else { - *reinterpret_cast<uint64_t*>(ptr) = reinterpret_cast64<uint64_t>(updated); - } - } else { - DCHECK_EQ(updated, orig); - } - } -} - } // namespace art #endif // ART_RUNTIME_OAT_IMAGE_INL_H_ diff --git a/runtime/oat/image.cc b/runtime/oat/image.cc index 7bdc4f8e50..a7ac8e0ebd 100644 --- a/runtime/oat/image.cc +++ b/runtime/oat/image.cc @@ -34,8 +34,8 @@ namespace art HIDDEN { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -// Last change: Add JniStubMethodsSection. -const uint8_t ImageHeader::kImageVersion[] = { '1', '1', '0', '\0' }; +// Last change: Split intrinsics list - with and without HIR. +const uint8_t ImageHeader::kImageVersion[] = { '1', '0', '9', '\0' }; ImageHeader::ImageHeader(uint32_t image_reservation_size, uint32_t component_count, @@ -264,10 +264,9 @@ const char* ImageHeader::GetImageSectionName(ImageSections index) { case kSectionObjects: return "Objects"; case kSectionArtFields: return "ArtFields"; case kSectionArtMethods: return "ArtMethods"; + case kSectionRuntimeMethods: return "RuntimeMethods"; case kSectionImTables: return "ImTables"; case kSectionIMTConflictTables: return "IMTConflictTables"; - case kSectionRuntimeMethods: return "RuntimeMethods"; - case kSectionJniStubMethods: return "JniStubMethods"; case kSectionInternedStrings: return "InternedStrings"; case kSectionClassTable: return "ClassTable"; case kSectionStringReferenceOffsets: return "StringReferenceOffsets"; diff --git a/runtime/oat/image.h b/runtime/oat/image.h index cd479f1a81..23c92a1aa8 100644 --- a/runtime/oat/image.h +++ b/runtime/oat/image.h @@ -263,10 +263,9 @@ class PACKED(8) ImageHeader { kSectionObjects, kSectionArtFields, kSectionArtMethods, + kSectionRuntimeMethods, kSectionImTables, kSectionIMTConflictTables, - kSectionRuntimeMethods, - kSectionJniStubMethods, kSectionInternedStrings, kSectionClassTable, kSectionStringReferenceOffsets, @@ -336,10 +335,6 @@ class PACKED(8) ImageHeader { return GetImageSection(kSectionMetadata); } - const ImageSection& GetJniStubMethodsSection() const { - return GetImageSection(kSectionJniStubMethods); - } - const ImageSection& GetImageBitmapSection() const { return GetImageSection(kSectionImageBitmap); } @@ -408,11 +403,6 @@ class PACKED(8) ImageHeader { uint8_t* base, PointerSize pointer_size) const; - template <bool kUpdate = false, typename Visitor> - void VisitJniStubMethods(const Visitor& visitor, - uint8_t* base, - PointerSize pointer_size) const REQUIRES_SHARED(Locks::mutator_lock_); - IterationRange<const Block*> GetBlocks() const { return GetBlocks(GetImageBegin()); } diff --git a/runtime/oat/jni_stub_hash_map.cc b/runtime/oat/jni_stub_hash_map.cc deleted file mode 100644 index fccf697b7d..0000000000 --- a/runtime/oat/jni_stub_hash_map.cc +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jni_stub_hash_map.h" - -#include "arch/arm64/jni_frame_arm64.h" -#include "arch/instruction_set.h" -#include "arch/riscv64/jni_frame_riscv64.h" -#include "arch/x86_64/jni_frame_x86_64.h" -#include "base/macros.h" - -namespace art HIDDEN { - -static char TranslateArgToJniShorty(char ch) { - // Byte, char, int, short, boolean are treated the same(e.g., Wx registers for arm64) when - // generating JNI stub, so their JNI shorty characters are same. - // ABCDEFGHIJKLMNOPQRSTUVWXYZ - static constexpr char kTranslations[] = ".PPD.F..PJ.L......P......P"; - DCHECK_GE(ch, 'A'); - DCHECK_LE(ch, 'Z'); - DCHECK_NE(kTranslations[ch - 'A'], '.'); - return kTranslations[ch - 'A']; -} - -static char TranslateReturnTypeToJniShorty(char ch, InstructionSet isa = InstructionSet::kNone) { - // For all archs, reference type has a different JNI shorty character than others as it needs to - // be decoded in stub. - // For arm64, small return types need sign-/zero-extended. - // For x86_64, small return types need sign-/zero-extended, and RAX needs to be preserved and - // restored when thread state changes. - // Other archs keeps untranslated for simplicity. - // TODO: support riscv64 with an optimized version. - // ABCDEFGHIJKLMNOPQRSTUVWXYZ - static constexpr char kArm64Translations[] = ".BCP.P..PP.L......S..P...Z"; - static constexpr char kX86_64Translations[] = ".BCP.P..RR.L......S..P...Z"; - static constexpr char kOtherTranslations[] = ".BCD.F..IJ.L......S..V...Z"; - DCHECK_GE(ch, 'A'); - DCHECK_LE(ch, 'Z'); - switch (isa) { - case InstructionSet::kArm64: - DCHECK_NE(kArm64Translations[ch - 'A'], '.'); - return kArm64Translations[ch - 'A']; - case InstructionSet::kX86_64: - DCHECK_NE(kX86_64Translations[ch - 'A'], '.'); - return kX86_64Translations[ch - 'A']; - default: - DCHECK_NE(kOtherTranslations[ch - 'A'], '.'); - return kOtherTranslations[ch - 'A']; - } -} - -static constexpr size_t GetMaxIntLikeRegisterArgs(InstructionSet isa) { - switch (isa) { - case InstructionSet::kArm64: - return arm64::kMaxIntLikeRegisterArguments; - case InstructionSet::kX86_64: - return x86_64::kMaxIntLikeRegisterArguments; - default: - LOG(FATAL) << "Unrecognized isa: " << isa << " for " << __FUNCTION__; - UNREACHABLE(); - } -} - -static constexpr size_t GetMaxFloatOrDoubleRegisterArgs(InstructionSet isa) { - switch (isa) { - case InstructionSet::kArm64: - return arm64::kMaxFloatOrDoubleRegisterArguments; - case InstructionSet::kX86_64: - return x86_64::kMaxFloatOrDoubleRegisterArguments; - default: - LOG(FATAL) << "Unrecognized isa: " << isa << " for " << __FUNCTION__; - UNREACHABLE(); - } -} - -static size_t StackOffset(char ch) { - if (ch == 'J' || ch == 'D') { - return 8; - } else { - return 4; - } -} - -static bool IsFloatOrDoubleArg(char ch) { - return ch == 'F' || ch == 'D'; -} - -static bool IsIntegralArg(char ch) { - return ch == 'B' || ch == 'C' || ch == 'I' || ch == 'J' || ch == 'S' || ch == 'Z'; -} - -static bool IsReferenceArg(char ch) { - return ch == 'L'; -} - -template<InstructionSet kIsa> -size_t JniStubKeyOptimizedHash(const JniStubKey& key) { - bool is_static = key.Flags() & kAccStatic; - std::string_view shorty = key.Shorty(); - size_t result = key.Flags(); - result ^= TranslateReturnTypeToJniShorty(shorty[0], kIsa); - constexpr size_t kMaxFloatOrDoubleRegisterArgs = GetMaxFloatOrDoubleRegisterArgs(kIsa); - constexpr size_t kMaxIntLikeRegisterArgs = GetMaxIntLikeRegisterArgs(kIsa); - size_t float_or_double_args = 0; - // ArtMethod* and 'Object* this' for non-static method. - // ArtMethod* for static method. - size_t int_like_args = is_static ? 1 : 2; - size_t stack_offset = 0; - for (char c : shorty.substr(1u)) { - bool stack_offset_matters = false; - stack_offset += StackOffset(c); - if (IsFloatOrDoubleArg(c)) { - ++float_or_double_args; - if (float_or_double_args > kMaxFloatOrDoubleRegisterArgs) { - // Stack offset matters if we run out of float-like (float, double) argument registers - // because the subsequent float-like args should be passed on the stack. - stack_offset_matters = true; - } else { - // Floating-point register arguments are not touched when generating JNI stub, so could be - // ignored when calculating hash value. - continue; - } - } else { - ++int_like_args; - if (int_like_args > kMaxIntLikeRegisterArgs || IsReferenceArg(c)) { - // Stack offset matters if we run out of integer-like (pointer, object, long, int, short, - // bool, etc) argument registers because the subsequent integer-like args should be passed - // on the stack. It also matters if current arg is reference type because it needs to be - // spilled as raw data even if it's in a register. - stack_offset_matters = true; - } else if (!is_static) { - // For non-static method, two managed arguments 'ArtMethod*' and 'Object* this' correspond - // to two native arguments 'JNIEnv*' and 'jobject'. So trailing integral (long, int, short, - // bool, etc) arguments will remain in the same registers, which do not need any generated - // code. - // But for static method, we have only one leading managed argument 'ArtMethod*' but two - // native arguments 'JNIEnv*' and 'jclass'. So trailing integral arguments are always - // shuffled around and affect the generated code. - continue; - } - } - // int_like_args is needed for reference type because it will determine from which register - // we take the value to construct jobject. - if (IsReferenceArg(c)) { - result = result * 31u * int_like_args ^ TranslateArgToJniShorty(c); - } else { - result = result * 31u ^ TranslateArgToJniShorty(c); - } - if (stack_offset_matters) { - result += stack_offset; - } - } - return result; -} - -size_t JniStubKeyGenericHash(const JniStubKey& key) { - std::string_view shorty = key.Shorty(); - size_t result = key.Flags(); - result ^= TranslateReturnTypeToJniShorty(shorty[0]); - for (char c : shorty.substr(1u)) { - result = result * 31u ^ TranslateArgToJniShorty(c); - } - return result; -} - -JniStubKeyHash::JniStubKeyHash(InstructionSet isa) { - switch (isa) { - case InstructionSet::kArm: - case InstructionSet::kThumb2: - case InstructionSet::kRiscv64: - case InstructionSet::kX86: - hash_func_ = JniStubKeyGenericHash; - break; - case InstructionSet::kArm64: - hash_func_ = JniStubKeyOptimizedHash<InstructionSet::kArm64>; - break; - case InstructionSet::kX86_64: - hash_func_ = JniStubKeyOptimizedHash<InstructionSet::kX86_64>; - break; - case InstructionSet::kNone: - LOG(FATAL) << "No instruction set given for " << __FUNCTION__; - UNREACHABLE(); - } -} - -template<InstructionSet kIsa> -bool JniStubKeyOptimizedEquals(const JniStubKey& lhs, const JniStubKey& rhs) { - if (lhs.Flags() != rhs.Flags()) { - return false; - } - std::string_view shorty_lhs = lhs.Shorty(); - std::string_view shorty_rhs = rhs.Shorty(); - if (TranslateReturnTypeToJniShorty(shorty_lhs[0], kIsa) != - TranslateReturnTypeToJniShorty(shorty_rhs[0], kIsa)) { - return false; - } - bool is_static = lhs.Flags() & kAccStatic; - constexpr size_t kMaxFloatOrDoubleRegisterArgs = GetMaxFloatOrDoubleRegisterArgs(kIsa); - constexpr size_t kMaxIntLikeRegisterArgs = GetMaxIntLikeRegisterArgs(kIsa); - size_t float_or_double_args_lhs = 0; - size_t float_or_double_args_rhs = 0; - size_t int_like_args_lhs = is_static ? 1 : 2; - size_t int_like_args_rhs = is_static ? 1 : 2; - size_t stack_offset_lhs = 0; - size_t stack_offset_rhs = 0; - size_t i = 1; - size_t j = 1; - while (i < shorty_lhs.length() && j < shorty_rhs.length()) { - bool should_skip = false; - bool stack_offset_matters = false; - char ch_lhs = shorty_lhs[i]; - char ch_rhs = shorty_rhs[j]; - - if (IsFloatOrDoubleArg(ch_lhs) && - float_or_double_args_lhs < kMaxFloatOrDoubleRegisterArgs) { - // Skip float-like register arguments. - ++i; - ++float_or_double_args_lhs; - stack_offset_lhs += StackOffset(ch_lhs); - should_skip = true; - } else if (IsIntegralArg(ch_lhs) && - int_like_args_lhs < kMaxIntLikeRegisterArgs) { - if (!is_static) { - // Skip integral register arguments for non-static method. - ++i; - ++int_like_args_lhs; - stack_offset_lhs += StackOffset(ch_lhs); - should_skip = true; - } - } else { - stack_offset_matters = true; - } - - if (IsFloatOrDoubleArg(ch_rhs) && - float_or_double_args_rhs < kMaxFloatOrDoubleRegisterArgs) { - // Skip float-like register arguments. - ++j; - ++float_or_double_args_rhs; - stack_offset_rhs += StackOffset(ch_rhs); - should_skip = true; - } else if (IsIntegralArg(ch_rhs) && - int_like_args_rhs < kMaxIntLikeRegisterArgs) { - if (!is_static) { - // Skip integral register arguments for non-static method. - ++j; - ++int_like_args_rhs; - stack_offset_rhs += StackOffset(ch_rhs); - should_skip = true; - } - } else { - stack_offset_matters = true; - } - - if (should_skip) { - continue; - } - if (TranslateArgToJniShorty(ch_lhs) != TranslateArgToJniShorty(ch_rhs)) { - return false; - } - if (stack_offset_matters && stack_offset_lhs != stack_offset_rhs) { - return false; - } - // int_like_args needs to be compared for reference type because it will determine from - // which register we take the value to construct jobject. - if (IsReferenceArg(ch_lhs) && int_like_args_lhs != int_like_args_rhs) { - return false; - } - // Passed character comparison. - ++i; - ++j; - stack_offset_lhs += StackOffset(ch_lhs); - stack_offset_rhs += StackOffset(ch_rhs); - DCHECK_EQ(IsFloatOrDoubleArg(ch_lhs), IsFloatOrDoubleArg(ch_rhs)); - if (IsFloatOrDoubleArg(ch_lhs)) { - ++float_or_double_args_lhs; - ++float_or_double_args_rhs; - } else { - ++int_like_args_lhs; - ++int_like_args_rhs; - } - } - auto remaining_shorty = - i < shorty_lhs.length() ? shorty_lhs.substr(i) : shorty_rhs.substr(j); - size_t float_or_double_args = - i < shorty_lhs.length() ? float_or_double_args_lhs : float_or_double_args_rhs; - size_t int_like_args = i < shorty_lhs.length() ? int_like_args_lhs : int_like_args_rhs; - for (char c : remaining_shorty) { - if (IsFloatOrDoubleArg(c) && float_or_double_args < kMaxFloatOrDoubleRegisterArgs) { - ++float_or_double_args; - continue; - } - if (!is_static && IsIntegralArg(c) && int_like_args < kMaxIntLikeRegisterArgs) { - ++int_like_args; - continue; - } - return false; - } - return true; -} - -bool JniStubKeyGenericEquals(const JniStubKey& lhs, const JniStubKey& rhs) { - if (lhs.Flags() != rhs.Flags()) { - return false; - } - std::string_view shorty_lhs = lhs.Shorty(); - std::string_view shorty_rhs = rhs.Shorty(); - if (TranslateReturnTypeToJniShorty(shorty_lhs[0]) != - TranslateReturnTypeToJniShorty(shorty_rhs[0])) { - return false; - } - if (shorty_lhs.length() != shorty_rhs.length()) { - return false; - } - for (size_t i = 1; i < shorty_lhs.length(); ++i) { - if (TranslateArgToJniShorty(shorty_lhs[i]) != TranslateArgToJniShorty(shorty_rhs[i])) { - return false; - } - } - return true; -} - -JniStubKeyEquals::JniStubKeyEquals(InstructionSet isa) { - switch (isa) { - case InstructionSet::kArm: - case InstructionSet::kThumb2: - case InstructionSet::kRiscv64: - case InstructionSet::kX86: - equals_func_ = JniStubKeyGenericEquals; - break; - case InstructionSet::kArm64: - equals_func_ = JniStubKeyOptimizedEquals<InstructionSet::kArm64>; - break; - case InstructionSet::kX86_64: - equals_func_ = JniStubKeyOptimizedEquals<InstructionSet::kX86_64>; - break; - case InstructionSet::kNone: - LOG(FATAL) << "No instruction set given for " << __FUNCTION__; - UNREACHABLE(); - } -} - -} // namespace art diff --git a/runtime/oat/jni_stub_hash_map.h b/runtime/oat/jni_stub_hash_map.h deleted file mode 100644 index 6e4603d3fb..0000000000 --- a/runtime/oat/jni_stub_hash_map.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_RUNTIME_OAT_JNI_STUB_HASH_MAP_H_ -#define ART_RUNTIME_OAT_JNI_STUB_HASH_MAP_H_ - -#include <memory> -#include <string_view> - -#include "arch/instruction_set.h" -#include "art_method.h" -#include "base/hash_map.h" - -namespace art HIDDEN { - -class JniStubKey { - public: - JniStubKey() = default; - JniStubKey(const JniStubKey& other) = default; - JniStubKey& operator=(const JniStubKey& other) = default; - - JniStubKey(uint32_t flags, std::string_view shorty) - : flags_(flags & (kAccStatic | kAccSynchronized | kAccFastNative | kAccCriticalNative)), - shorty_(shorty) { - DCHECK(ArtMethod::IsNative(flags)); - } - - explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) - : JniStubKey(method->GetAccessFlags(), method->GetShortyView()) {} - - uint32_t Flags() const { - return flags_; - } - - std::string_view Shorty() const { - return shorty_; - } - - bool IsEmpty() const { - return Shorty().empty(); - } - - void MakeEmpty() { - shorty_ = {}; - } - - private: - uint32_t flags_; - std::string_view shorty_; -}; - -template <typename Value> -class JniStubKeyEmpty { - public: - bool IsEmpty(const std::pair<JniStubKey, Value>& pair) const { - return pair.first.IsEmpty(); - } - - void MakeEmpty(std::pair<JniStubKey, Value>& pair) { - pair.first.MakeEmpty(); - } -}; - -using JniStubKeyHashFunction = size_t (*)(const JniStubKey& key); - -class JniStubKeyHash { - public: - EXPORT explicit JniStubKeyHash(InstructionSet isa); - - size_t operator()(const JniStubKey& key) const { - return hash_func_(key); - } - - private: - JniStubKeyHashFunction hash_func_; -}; - -using JniStubKeyEqualsFunction = bool (*)(const JniStubKey& lhs, const JniStubKey& rhs); - -class JniStubKeyEquals { - public: - EXPORT explicit JniStubKeyEquals(InstructionSet isa); - - bool operator()(const JniStubKey& lhs, const JniStubKey& rhs) const { - return equals_func_(lhs, rhs); - } - - private: - JniStubKeyEqualsFunction equals_func_; -}; - -template <typename Value, - typename Alloc = std::allocator<std::pair<JniStubKey, Value>>> -using JniStubHashMap = - HashMap<JniStubKey, Value, JniStubKeyEmpty<Value>, JniStubKeyHash, JniStubKeyEquals>; - -} // namespace art - -#endif // ART_RUNTIME_OAT_JNI_STUB_HASH_MAP_H_ diff --git a/runtime/oat/jni_stub_hash_map_test.cc b/runtime/oat/jni_stub_hash_map_test.cc deleted file mode 100644 index 010a25d860..0000000000 --- a/runtime/oat/jni_stub_hash_map_test.cc +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "jni_stub_hash_map.h" - -#include <gtest/gtest.h> - -#include <memory> -#include <ostream> -#include <string> -#include <string_view> -#include <utility> -#include <vector> - -#include "android-base/logging.h" -#include "android-base/stringprintf.h" -#include "arch/instruction_set.h" -#include "art_method.h" -#include "base/array_ref.h" -#include "base/locks.h" -#include "base/utils.h" -#include "class_linker.h" -#include "common_compiler_test.h" -#include "common_compiler_test.cc" -#include "compiler.h" -#include "gc/heap.h" -#include "gc/space/image_space.h" -#include "handle.h" -#include "handle_scope.h" -#include "handle_scope-inl.h" -#include "image.h" -#include "image-inl.h" -#include "jni.h" -#include "mirror/class.h" -#include "mirror/class_loader.h" -#include "mirror/dex_cache.h" -#include "obj_ptr.h" -#include "runtime.h" -#include "scoped_thread_state_change.h" -#include "strstream" - -namespace art HIDDEN { - -// Teach gtest how to print the ArrayRef<const uint8_t>. The customized output is easier used -// for converting to assembly instructions. -static void PrintTo(const ArrayRef<const uint8_t>& array, std::ostream* os) { - *os << "[[["; - for (const uint8_t& element : array) { - *os << " "; - *os << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(element); - } - *os << " ]]]"; -} - -class JniStubHashMapTest : public CommonCompilerTest { - protected: - JniStubHashMapTest() - : jni_stub_hash_map_(JniStubKeyHash(kRuntimeISA), JniStubKeyEquals(kRuntimeISA)) { - if (kRuntimeISA == InstructionSet::kArm64 || kRuntimeISA == InstructionSet::kX86_64) { - // Only arm64 and x86_64 use strict check. - strict_check_ = true; - } else { - // Other archs use loose check. - strict_check_ = false; - } - } - - void SetStrictCheck(bool value) { - strict_check_ = value; - } - - void SetUpForTest() { - ScopedObjectAccess soa(Thread::Current()); - jobject jclass_loader = LoadDex("MyClassNatives"); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader( - hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); - pointer_size_ = class_linker_->GetImagePointerSize(); - ObjPtr<mirror::Class> klass = - class_linker_->FindClass(soa.Self(), "LMyClassNatives;", class_loader); - ASSERT_TRUE(klass != nullptr); - jklass_ = soa.AddLocalReference<jclass>(klass); - } - - void SetBaseMethod(std::string_view base_method_name, std::string_view base_method_sig) { - jni_stub_hash_map_.clear(); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(jklass_); - base_method_ = klass->FindClassMethod(base_method_name, base_method_sig, pointer_size_); - ASSERT_TRUE(base_method_ != nullptr); - ASSERT_TRUE(base_method_->IsNative()); - - OneCompiledMethodStorage base_method_storage; - StackHandleScope<1> hs(self); - std::unique_ptr<Compiler> compiler( - Compiler::Create(*compiler_options_, &base_method_storage, compiler_kind_)); - const DexFile& dex_file = *base_method_->GetDexFile(); - Handle<mirror::DexCache> dex_cache = - hs.NewHandle(GetClassLinker()->FindDexCache(self, dex_file)); - compiler->JniCompile(base_method_->GetAccessFlags(), - base_method_->GetDexMethodIndex(), - dex_file, - dex_cache); - ArrayRef<const uint8_t> code = base_method_storage.GetCode(); - base_method_code_.assign(code.begin(), code.end()); - - jni_stub_hash_map_.insert(std::make_pair(JniStubKey(base_method_), base_method_)); - } - - void CompareMethod(std::string_view cmp_method_name, std::string_view cmp_method_sig) { - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(jklass_); - ArtMethod* cmp_method = klass->FindClassMethod(cmp_method_name, cmp_method_sig, pointer_size_); - ASSERT_TRUE(cmp_method != nullptr); - ASSERT_TRUE(cmp_method->IsNative()); - - OneCompiledMethodStorage cmp_method_storage; - StackHandleScope<1> hs(self); - std::unique_ptr<Compiler> compiler( - Compiler::Create(*compiler_options_, &cmp_method_storage, compiler_kind_)); - const DexFile& dex_file = *cmp_method->GetDexFile(); - Handle<mirror::DexCache> dex_cache = - hs.NewHandle(GetClassLinker()->FindDexCache(self, dex_file)); - compiler->JniCompile(cmp_method->GetAccessFlags(), - cmp_method->GetDexMethodIndex(), - dex_file, - dex_cache); - - ArrayRef<const uint8_t> method_code = ArrayRef<const uint8_t>(base_method_code_); - ArrayRef<const uint8_t> cmp_method_code = cmp_method_storage.GetCode(); - auto it = jni_stub_hash_map_.find(JniStubKey(cmp_method)); - if (it != jni_stub_hash_map_.end()) { - ASSERT_EQ(method_code, cmp_method_code) - << "base method: " << base_method_->PrettyMethod() << ", compared method: " - << cmp_method->PrettyMethod(); - } else if (strict_check_){ - // If the compared method maps to a different entry, then its compiled JNI stub should be - // also different from the base one. - ASSERT_NE(method_code, cmp_method_code) - << "base method: " << base_method_->PrettyMethod() << ", compared method: " - << cmp_method->PrettyMethod(); - } - } - - bool strict_check_; - JniStubHashMap<ArtMethod*> jni_stub_hash_map_; - PointerSize pointer_size_; - jclass jklass_; - ArtMethod* base_method_; - std::vector<uint8_t> base_method_code_; -}; - -class JniStubHashMapBootImageTest : public CommonRuntimeTest { - protected: - void SetUpRuntimeOptions(RuntimeOptions* options) override { - std::string runtime_args_image; - runtime_args_image = android::base::StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str()); - options->push_back(std::make_pair(runtime_args_image, nullptr)); - } -}; - -TEST_F(JniStubHashMapTest, ReturnType) { - SetUpForTest(); - SetBaseMethod("fooI", "(I)I"); - CompareMethod("fooI_V", "(I)V"); - CompareMethod("fooI_B", "(I)B"); - CompareMethod("fooI_C", "(I)C"); - CompareMethod("fooI_S", "(I)S"); - CompareMethod("fooI_Z", "(I)Z"); - CompareMethod("fooI_J", "(I)J"); - CompareMethod("fooI_F", "(I)F"); - CompareMethod("fooI_D", "(I)D"); - CompareMethod("fooI_L", "(I)Ljava/lang/Object;"); -} - -TEST_F(JniStubHashMapTest, ArgType) { - SetUpForTest(); - SetBaseMethod("sfooI", "(I)I"); - CompareMethod("sfooB", "(B)I"); - CompareMethod("sfooC", "(C)I"); - CompareMethod("sfooS", "(S)I"); - CompareMethod("sfooZ", "(Z)I"); - CompareMethod("sfooL", "(Ljava/lang/Object;)I"); -} - -TEST_F(JniStubHashMapTest, FloatingPointArg) { - SetUpForTest(); - SetBaseMethod("sfooI", "(I)I"); - CompareMethod("sfoo7FI", "(FFFFFFFI)I"); - CompareMethod("sfoo3F5DI", "(FFFDDDDDI)I"); - CompareMethod("sfoo3F6DI", "(FFFDDDDDDI)I"); -} - -TEST_F(JniStubHashMapTest, IntegralArg) { - SetUpForTest(); - SetBaseMethod("fooL", "(Ljava/lang/Object;)I"); - CompareMethod("fooL4I", "(Ljava/lang/Object;IIII)I"); - CompareMethod("fooL5I", "(Ljava/lang/Object;IIIII)I"); - CompareMethod("fooL3IJC", "(Ljava/lang/Object;IIIJC)I"); - CompareMethod("fooL3IJCS", "(Ljava/lang/Object;IIIJCS)I"); -} - -TEST_F(JniStubHashMapTest, StackOffsetMatters) { - SetUpForTest(); - SetBaseMethod("foo7FDF", "(FFFFFFFDF)I"); - CompareMethod("foo9F", "(FFFFFFFFF)I"); - CompareMethod("foo7FIFF", "(FFFFFFFIFF)I"); - SetBaseMethod("foo5IJI", "(IIIIIJI)I"); - CompareMethod("foo7I", "(IIIIIII)I"); - CompareMethod("foo5IFII", "(IIIIIFII)I"); - SetBaseMethod("fooFDL", "(FDLjava/lang/Object;)I"); - CompareMethod("foo2FL", "(FFLjava/lang/Object;)I"); - CompareMethod("foo3FL", "(FFFLjava/lang/Object;)I"); - CompareMethod("foo2FIL", "(FFILjava/lang/Object;)I"); -} - -TEST_F(JniStubHashMapTest, IntLikeRegsMatters) { - SetUpForTest(); - SetBaseMethod("fooICFL", "(ICFLjava/lang/Object;)I"); - CompareMethod("foo2IFL", "(IIFLjava/lang/Object;)I"); - CompareMethod("fooICIL", "(ICILjava/lang/Object;)I"); -} - -TEST_F(JniStubHashMapTest, FastNative) { - SetUpForTest(); - SetBaseMethod("fooI_Fast", "(I)I"); - CompareMethod("fooI_Z_Fast", "(I)Z"); - CompareMethod("fooI_J_Fast", "(I)J"); - SetBaseMethod("fooICFL_Fast", "(ICFLjava/lang/Object;)I"); - CompareMethod("foo2IFL_Fast", "(IIFLjava/lang/Object;)I"); - CompareMethod("fooICIL_Fast", "(ICILjava/lang/Object;)I"); - SetBaseMethod("fooFDL_Fast", "(FDLjava/lang/Object;)I"); - CompareMethod("foo2FL_Fast", "(FFLjava/lang/Object;)I"); - CompareMethod("foo3FL_Fast", "(FFFLjava/lang/Object;)I"); - CompareMethod("foo2FIL_Fast", "(FFILjava/lang/Object;)I"); - SetBaseMethod("foo7F_Fast", "(FFFFFFF)I"); - CompareMethod("foo3F5D_Fast", "(FFFDDDDD)I"); - CompareMethod("foo3F6D_Fast", "(FFFDDDDDD)I"); - SetBaseMethod("fooL5I_Fast", "(Ljava/lang/Object;IIIII)I"); - CompareMethod("fooL3IJC_Fast", "(Ljava/lang/Object;IIIJC)I"); - CompareMethod("fooL3IJCS_Fast", "(Ljava/lang/Object;IIIJCS)I"); -} - -TEST_F(JniStubHashMapTest, CriticalNative) { - SetUpForTest(); - if (kRuntimeISA == InstructionSet::kX86_64) { - // In x86_64, the return type seems be ignored in critical function. - SetStrictCheck(false); - } - SetBaseMethod("returnInt_Critical", "()I"); - CompareMethod("returnDouble_Critical", "()D"); - CompareMethod("returnLong_Critical", "()J"); - SetBaseMethod("foo7F_Critical", "(FFFFFFF)I"); - CompareMethod("foo3F5D_Critical", "(FFFDDDDD)I"); - CompareMethod("foo3F6D_Critical", "(FFFDDDDDD)I"); -} - -TEST_F(JniStubHashMapBootImageTest, BootImageSelfCheck) { - std::vector<gc::space::ImageSpace*> image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - ASSERT_TRUE(!image_spaces.empty()); - for (gc::space::ImageSpace* space : image_spaces) { - const ImageHeader& header = space->GetImageHeader(); - PointerSize ptr_size = class_linker_->GetImagePointerSize(); - auto visitor = [&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) { - if (method.IsNative() && !method.IsIntrinsic()) { - const void* boot_jni_stub = class_linker_->FindBootJniStub(JniStubKey(&method)); - if (boot_jni_stub != nullptr) { - const void* cmp_jni_stub = method.GetOatMethodQuickCode(ptr_size); - size_t boot_jni_stub_size = - OatQuickMethodHeader::FromEntryPoint(boot_jni_stub)->GetCodeSize(); - size_t cmp_jni_stub_size = - OatQuickMethodHeader::FromEntryPoint(cmp_jni_stub)->GetCodeSize(); - ArrayRef<const uint8_t> boot_jni_stub_array = ArrayRef( - reinterpret_cast<const uint8_t*>(EntryPointToCodePointer(boot_jni_stub)), - boot_jni_stub_size); - ArrayRef<const uint8_t> cmp_jni_stub_array = ArrayRef( - reinterpret_cast<const uint8_t*>(EntryPointToCodePointer(cmp_jni_stub)), - cmp_jni_stub_size); - ASSERT_EQ(boot_jni_stub_array, cmp_jni_stub_array) - << "method: " << method.PrettyMethod() << ", size = " << cmp_jni_stub_size; - } - } - }; - header.VisitPackedArtMethods(visitor, space->Begin(), ptr_size); - } -} - -} // namespace art diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc index 83e020090e..5d304698bf 100644 --- a/runtime/runtime_image.cc +++ b/runtime/runtime_image.cc @@ -955,16 +955,7 @@ class RuntimeImageHelper { const OatFile* oat_file = image_spaces[0]->GetOatFile(); DCHECK(oat_file != nullptr); const OatHeader& header = oat_file->GetOatHeader(); - const void* entrypoint = header.GetOatAddress(stub); - if (method->IsNative() && (is_class_initialized || !method->NeedsClinitCheckBeforeCall())) { - // Use boot JNI stub if found. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const void* boot_jni_stub = class_linker->FindBootJniStub(method); - if (boot_jni_stub != nullptr) { - entrypoint = boot_jni_stub; - } - } - copy->SetEntryPointFromQuickCompiledCode(entrypoint); + copy->SetEntryPointFromQuickCompiledCode(header.GetOatAddress(stub)); if (method->IsNative()) { StubType stub_type = method->IsCriticalNative() diff --git a/runtime/stack.cc b/runtime/stack.cc index 81c8a8a9e0..942b155261 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -840,7 +840,7 @@ void StackVisitor::WalkStack(bool include_transitions) { cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); } else { // We are sure we are not running GenericJni here. Though the entry point could still be - // GenericJnistub. The entry point is usually JITed or AOT code. It could be also a + // GenericJnistub. The entry point is usually JITed or AOT code. It could be lso a // resolution stub if the class isn't visibly initialized yet. const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); CHECK(existing_entry_point != nullptr); @@ -856,22 +856,13 @@ void StackVisitor::WalkStack(bool include_transitions) { if (code != nullptr) { cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); } else { - // This may be either a JITed JNI stub frame or boot JNI stub frame. For - // non-debuggable runtimes we will generate JIT stubs if there are no AOT stubs or - // boot stubs for native methods. Since we checked for AOT code earlier, we must be - // running JITed code or boot stub code. For debuggable runtimes we might have JIT - // code even when AOT stub or boot stub is present but we tag SP in JITed JNI stubs + // This must be a JITted JNI stub frame. For non-debuggable runtimes we only generate + // JIT stubs if there are no AOT stubs for native methods. Since we checked for AOT + // code earlier, we must be running JITed code. For debuggable runtimes we might have + // JIT code even when AOT code is present but we tag SP in JITed JNI stubs // in debuggable runtimes. This case is handled earlier. - if (runtime->GetJit() != nullptr) { - code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); - } - if (code == nullptr) { - // Check if current method uses the boot JNI stub. - const void* boot_jni_stub = class_linker->FindBootJniStub(method); - if (boot_jni_stub != nullptr) { - code = boot_jni_stub; - } - } + CHECK(runtime->GetJit() != nullptr); + code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); CHECK(code != nullptr) << method->PrettyMethod(); cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); } diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java index c0829b5100..179e186f6a 100644 --- a/test/667-jit-jni-stub/src/Main.java +++ b/test/667-jit-jni-stub/src/Main.java @@ -24,8 +24,6 @@ public class Main { return; } - // Deoptimize callThrough() to ensure it can be JITed afterwards. - deoptimizeNativeMethod(Main.class, "callThrough"); testCompilationUseAndCollection(); testMixedFramesOnStack(); } @@ -138,7 +136,7 @@ public class Main { if (++count == 50) { throw new Error("TIMEOUT"); } - } + }; } public static void assertTrue(boolean value) { @@ -156,10 +154,9 @@ public class Main { public static void doNothing() { } public static void throwError() { throw new Error(); } - // Note that the callThrough()'s shorty differs from shorties of the other native methods used - // in this test (except deoptimizeNativeMethod) because of the return type `void.` + // Note that the callThrough()'s shorty differs from shorties of the other + // native methods used in this test because of the return type `void.` public native static void callThrough(Class<?> cls, String methodName); - public native static void deoptimizeNativeMethod(Class<?> cls, String methodName); public native static void jitGc(); public native static boolean isNextJitGcFull(); diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java index 787f53362a..d3bf5ff98e 100644 --- a/test/MyClassNatives/MyClassNatives.java +++ b/test/MyClassNatives/MyClassNatives.java @@ -37,38 +37,6 @@ class MyClassNatives { // Normal native native int fooI(int x); // Normal native - native int fooL(Object x); - // Normal native - native void fooI_V(int x); - // Normal native - native byte fooI_B(int x); - // Normal native - native char fooI_C(int x); - // Normal native - native short fooI_S(int x); - // Normal native - native boolean fooI_Z(int x); - // Normal native - native long fooI_J(int x); - // Normal native - native float fooI_F(int x); - // Normal native - native double fooI_D(int x); - // Normal native - native Object fooI_L(int x); - // Normal native - static native int sfooI(int x); - // Normal native - static native int sfooB(byte x); - // Normal native - static native int sfooC(char x); - // Normal native - static native int sfooS(short x); - // Normal native - static native int sfooZ(boolean x); - // Normal native - static native int sfooL(Object x); - // Normal native native int fooII(int x, int y); // Normal native native long fooJJ(long x, long y); @@ -188,52 +156,6 @@ class MyClassNatives { // Normal native static native long returnLong(); - // Normal native - static native int sfoo7FI(float f1, float f2, float f3, float f4, float f5, float f6, - float f7, int i1); - // Normal native - static native int sfoo3F5DI(float f1, float f2, float f3, double d1, double d2, double d3, - double d4, double d5, int i1); - // Normal native - static native int sfoo3F6DI(float f1, float f2, float f3, double d1, double d2, double d3, - double d4, double d5, double d6, int i1); - // Normal native - native int fooL4I(Object o1, int i1, int i2, int i3, int i4); - // Normal native - native int fooL5I(Object o1, int i1, int i2, int i3, int i4, int i5); - // Normal native - native int fooL3IJC(Object o1, int i1, int i2, int i3, long l1, char c1); - // Normal native - native int fooL3IJCS(Object o1, int i1, int i2, int i3, long l1, char c1, short s1); - // Normal native - native int foo9F(float f1, float f2, float f3, float f4, float f5, float f6, float f7, - float f8, float f9); - // Normal native - native int foo7FDF(float f1, float f2, float f3, float f4, float f5, float f6, float f7, - double d1, float f8); - // Normal native - native int foo7FIFF(float f1, float f2, float f3, float f4, float f5, float f6, float f7, - int i1, float f8, float f9); - // Normal native - native int foo7I(int i1, int i2, int i3, int i4, int i5, int i6, int i7); - // Normal native - native int foo5IJI(int i1, int i2, int i3, int i4, int i5, long l1, int i6); - // Normal native - native int foo5IFII(int i1, int i2, int i3, int i4, int i5, float f1, int i6, int i7); - // Normal native - native int foo2FL(float f1, float f2, Object o1); - // Normal native - native int fooFDL(float f1, double f2, Object o1); - // Normal native - native int foo3FL(float f1, float f2, float f3, Object o1); - // Normal native - native int foo2FIL(float f1, float f2, int i1, Object o1); - // Normal native - native int foo2IFL(int i1, int i2, float f1, Object o1); - // Normal native - native int fooICFL(int i1, char c1, float f1, Object o1); - // Normal native - native int fooICIL(int i1, char c1, int i2, Object o1); @FastNative @@ -362,38 +284,6 @@ class MyClassNatives { @FastNative static native long returnLong_Fast(); - @FastNative - native boolean fooI_Z_Fast(int i); - @FastNative - native long fooI_J_Fast(int i); - @FastNative - native int fooICFL_Fast(int i1, char c1, float f1, Object o1); - @FastNative - native int foo2IFL_Fast(int i1, int i2, float f1, Object o1); - @FastNative - native int fooICIL_Fast(int i1, char c1, int i2, Object o1); - @FastNative - native int fooFDL_Fast(float f1, double d1, Object o1); - @FastNative - native int foo2FL_Fast(float f1, float f2, Object o1); - @FastNative - native int foo3FL_Fast(float f1, float f2, float f3, Object o1); - @FastNative - native int foo2FIL_Fast(float f1, float f2, int i1, Object o1); - @FastNative - native int foo7F_Fast(float f1, float f2, float f3, float f4, float f5, float f6, float f7); - @FastNative - native int foo3F5D_Fast(float f1, float f2, float f3, double d1, double d2, double d3, - double d4, double d5); - @FastNative - native int foo3F6D_Fast(float f1, float f2, float f3, double d1, double d2, double d3, - double d4, double d5, double d6); - @FastNative - native int fooL5I_Fast(Object o1, int i1, int i2, int i3, int i4, int i5); - @FastNative - native int fooL3IJC_Fast(Object o1, int i1, int i2, int i3, long l1, char c1); - @FastNative - native int fooL3IJCS_Fast(Object o1, int i1, int i2, int i3, long l1, char c1, short s1); @CriticalNative @@ -436,15 +326,6 @@ class MyClassNatives { @CriticalNative static native long returnLong_Critical(); - @CriticalNative - static native int foo7F_Critical(float f1, float f2, float f3, float f4, float f5, float f6, - float f7); - @CriticalNative - static native int foo3F5D_Critical(float f1, float f2, float f3, double d1, double d2, - double d3, double d4, double d5); - @CriticalNative - static native int foo3F6D_Critical(float f1, float f2, float f3, double d1, double d2, - double d3, double d4, double d5, double d6); // Check for @FastNative/@CriticalNative annotation presence [or lack of presence]. diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index ed0cc3e1c4..a385f4c8db 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -443,22 +443,6 @@ extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeBootImage(JNIEnv*, jclass) Runtime::Current()->DeoptimizeBootImage(); } -extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeNativeMethod(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { - Thread* self = Thread::Current(); - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - // Make initialized classes visibly initialized to avoid entrypoint being set to boot JNI stub - // after deoptimize. - class_linker->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true); - ScopedObjectAccess soa(self); - ScopedUtfChars chars(env, method_name); - ArtMethod* method = GetMethod(soa, cls, chars); - CHECK(method->IsNative()); - Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr); -} - extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDebuggable(JNIEnv*, jclass) { return Runtime::Current()->IsJavaDebuggable() ? JNI_TRUE : JNI_FALSE; } |