diff options
author | 2021-01-25 08:43:57 +0000 | |
---|---|---|
committer | 2021-01-27 08:29:40 +0000 | |
commit | c39af9409ed4c8dd711be606f758a9b378cd0ee3 (patch) | |
tree | 43f8cc23a07a41fb8fc03be77a15fa203e27194d | |
parent | 3a73ffb70151dbc99fa41f300a237f8c29783e0e (diff) |
Add a nterp trampoline in the oat file, replaced at runtime.
To avoid checking whether an ArtMethod can run nterp when loading an
image, record that information directly in the image, and patch the
entrypoint so nterp can do direct pointer checks when calling an
ArtMethod.
Test: test.py
Bug: 177444058
Change-Id: Ia87367c37848e1efe33336b3677490c07d7e1767
-rw-r--r-- | dex2oat/driver/compiler_driver.cc | 8 | ||||
-rw-r--r-- | dex2oat/driver/compiler_driver.h | 1 | ||||
-rw-r--r-- | dex2oat/linker/image_writer.cc | 15 | ||||
-rw-r--r-- | dex2oat/linker/image_writer.h | 3 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer.cc | 5 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer.h | 2 | ||||
-rw-r--r-- | dex2oat/linker/oat_writer_test.cc | 2 | ||||
-rw-r--r-- | oatdump/oatdump.cc | 4 | ||||
-rw-r--r-- | runtime/class_linker.cc | 25 | ||||
-rw-r--r-- | runtime/class_linker.h | 6 | ||||
-rw-r--r-- | runtime/image.cc | 4 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp.cc | 12 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp.h | 1 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp_stub.cc | 5 | ||||
-rw-r--r-- | runtime/nterp_helpers.cc | 63 | ||||
-rw-r--r-- | runtime/nterp_helpers.h | 9 | ||||
-rw-r--r-- | runtime/oat.cc | 21 | ||||
-rw-r--r-- | runtime/oat.h | 8 |
18 files changed, 146 insertions, 48 deletions
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc index 3175ccbb78..605953c4be 100644 --- a/dex2oat/driver/compiler_driver.cc +++ b/dex2oat/driver/compiler_driver.cc @@ -324,6 +324,14 @@ std::unique_ptr<const std::vector<uint8_t>> CompilerDriver::CreateQuickToInterpr const { CREATE_TRAMPOLINE(QUICK, kQuickAbi, pQuickToInterpreterBridge) } + +std::unique_ptr<const std::vector<uint8_t>> CompilerDriver::CreateNterpTrampoline() + const { + // We use QuickToInterpreterBridge to not waste one word in the Thread object. + // The Nterp trampoline gets replaced with the nterp entrypoint when loading + // an image. + CREATE_TRAMPOLINE(QUICK, kQuickAbi, pQuickToInterpreterBridge) +} #undef CREATE_TRAMPOLINE void CompilerDriver::CompileAll(jobject class_loader, diff --git a/dex2oat/driver/compiler_driver.h b/dex2oat/driver/compiler_driver.h index 3008623ba2..137f6f138e 100644 --- a/dex2oat/driver/compiler_driver.h +++ b/dex2oat/driver/compiler_driver.h @@ -128,6 +128,7 @@ class CompilerDriver { std::unique_ptr<const std::vector<uint8_t>> CreateQuickImtConflictTrampoline() const; std::unique_ptr<const std::vector<uint8_t>> CreateQuickResolutionTrampoline() const; std::unique_ptr<const std::vector<uint8_t>> CreateQuickToInterpreterBridge() const; + std::unique_ptr<const std::vector<uint8_t>> CreateNterpTrampoline() const; ClassStatus GetClassStatus(const ClassReference& ref) const; bool GetCompiledClass(const ClassReference& ref, ClassStatus* status) const; diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 7bdeac2230..8b00f7b17a 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -72,6 +72,7 @@ #include "mirror/object_array-alloc-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" +#include "nterp_helpers.h" #include "oat.h" #include "oat_file.h" #include "oat_file_manager.h" @@ -2997,6 +2998,8 @@ const uint8_t* ImageWriter::GetOatAddress(StubType type) const { return static_cast<const uint8_t*>(header.GetQuickResolutionTrampoline()); case StubType::kQuickToInterpreterBridge: return static_cast<const uint8_t*>(header.GetQuickToInterpreterBridge()); + case StubType::kNterpTrampoline: + return static_cast<const uint8_t*>(header.GetNterpTrampoline()); default: UNREACHABLE(); } @@ -3032,9 +3035,13 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima if (quick_code == nullptr) { // If we don't have code, use generic jni / interpreter bridge. // Both perform class initialization check if needed. - quick_code = method->IsNative() - ? GetOatAddress(StubType::kQuickGenericJNITrampoline) - : GetOatAddress(StubType::kQuickToInterpreterBridge); + if (method->IsNative()) { + quick_code = GetOatAddress(StubType::kQuickGenericJNITrampoline); + } else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) { + quick_code = GetOatAddress(StubType::kNterpTrampoline); + } else { + quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge); + } } else if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) { // If we do have code but the method needs a class initialization check before calling @@ -3255,6 +3262,8 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea oat_header.GetQuickResolutionTrampolineOffset()); cur_image_info.SetStubOffset(StubType::kQuickToInterpreterBridge, oat_header.GetQuickToInterpreterBridgeOffset()); + cur_image_info.SetStubOffset(StubType::kNterpTrampoline, + oat_header.GetNterpTrampolineOffset()); } } diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 7a11fe671e..d65a9770d4 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -232,7 +232,8 @@ class ImageWriter final { kQuickIMTConflictTrampoline, kQuickResolutionTrampoline, kQuickToInterpreterBridge, - kLast = kQuickToInterpreterBridge, + kNterpTrampoline, + kLast = kNterpTrampoline, }; friend std::ostream& operator<<(std::ostream& stream, StubType stub_type); diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index aa1ed936c8..0a37b40925 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -438,6 +438,7 @@ OatWriter::OatWriter(const CompilerOptions& compiler_options, size_quick_imt_conflict_trampoline_(0), size_quick_resolution_trampoline_(0), size_quick_to_interpreter_bridge_(0), + size_nterp_trampoline_(0), size_trampoline_alignment_(0), size_method_header_(0), size_code_(0), @@ -2313,6 +2314,7 @@ size_t OatWriter::InitOatCode(size_t offset) { DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline); DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline); DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge); + DO_TRAMPOLINE(nterp_trampoline_, NterpTrampoline); #undef DO_TRAMPOLINE } else { @@ -2322,6 +2324,7 @@ size_t OatWriter::InitOatCode(size_t offset) { oat_header_->SetQuickImtConflictTrampolineOffset(0); oat_header_->SetQuickResolutionTrampolineOffset(0); oat_header_->SetQuickToInterpreterBridgeOffset(0); + oat_header_->SetNterpTrampolineOffset(0); } return offset; } @@ -2808,6 +2811,7 @@ bool OatWriter::CheckOatSize(OutputStream* out, size_t file_offset, size_t relat DO_STAT(size_quick_imt_conflict_trampoline_); DO_STAT(size_quick_resolution_trampoline_); DO_STAT(size_quick_to_interpreter_bridge_); + DO_STAT(size_nterp_trampoline_); DO_STAT(size_trampoline_alignment_); DO_STAT(size_method_header_); DO_STAT(size_code_); @@ -3185,6 +3189,7 @@ size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relati DO_TRAMPOLINE(quick_imt_conflict_trampoline_); DO_TRAMPOLINE(quick_resolution_trampoline_); DO_TRAMPOLINE(quick_to_interpreter_bridge_); + DO_TRAMPOLINE(nterp_trampoline_); #undef DO_TRAMPOLINE } return relative_offset; diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 2fd75719f4..6b2b4950ae 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -492,6 +492,7 @@ class OatWriter { std::unique_ptr<const std::vector<uint8_t>> quick_imt_conflict_trampoline_; std::unique_ptr<const std::vector<uint8_t>> quick_resolution_trampoline_; std::unique_ptr<const std::vector<uint8_t>> quick_to_interpreter_bridge_; + std::unique_ptr<const std::vector<uint8_t>> nterp_trampoline_; // output stats uint32_t size_vdex_header_; @@ -514,6 +515,7 @@ class OatWriter { uint32_t size_quick_imt_conflict_trampoline_; uint32_t size_quick_resolution_trampoline_; uint32_t size_quick_to_interpreter_bridge_; + uint32_t size_nterp_trampoline_; uint32_t size_trampoline_alignment_; uint32_t size_method_header_; uint32_t size_code_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index c82b7add65..614cfc4088 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -509,7 +509,7 @@ TEST_F(OatTest, WriteRead) { TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion - EXPECT_EQ(60U, sizeof(OatHeader)); + EXPECT_EQ(64U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(8U, sizeof(OatQuickMethodHeader)); EXPECT_EQ(169 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 2d95a8aa04..6c7f6102d8 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -201,6 +201,7 @@ class OatSymbolizer final { DO_TRAMPOLINE(QuickImtConflictTrampoline); DO_TRAMPOLINE(QuickResolutionTrampoline); DO_TRAMPOLINE(QuickToInterpreterBridge); + DO_TRAMPOLINE(NterpTrampoline); #undef DO_TRAMPOLINE Walk(); @@ -458,6 +459,8 @@ class OatDumper { GetQuickResolutionTrampolineOffset); DUMP_OAT_HEADER_OFFSET("QUICK TO INTERPRETER BRIDGE", GetQuickToInterpreterBridgeOffset); + DUMP_OAT_HEADER_OFFSET("NTERP_TRAMPOLINE", + GetNterpTrampolineOffset); #undef DUMP_OAT_HEADER_OFFSET // Print the key-value store. @@ -2137,6 +2140,7 @@ class ImageDumper { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (class_linker->IsQuickResolutionStub(quick_code) || class_linker->IsQuickToInterpreterBridge(quick_code) || + class_linker->IsNterpTrampoline(quick_code) || class_linker->IsQuickGenericJniStub(quick_code) || class_linker->IsJniDlsymLookupStub(quick_code) || class_linker->IsJniDlsymLookupCriticalStub(quick_code)) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f396a0a84a..ac97946714 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -129,6 +129,7 @@ #include "mirror/var_handle.h" #include "native/dalvik_system_DexFile.h" #include "nativehelper/scoped_local_ref.h" +#include "nterp_helpers.h" #include "oat.h" #include "oat_file-inl.h" #include "oat_file.h" @@ -231,7 +232,7 @@ static void ChangeInterpreterBridgeToNterp(ArtMethod* method, ClassLinker* class REQUIRES_SHARED(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); if (class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()) && - interpreter::CanMethodUseNterp(method)) { + CanMethodUseNterp(method)) { if (method->GetDeclaringClass()->IsVisiblyInitialized() || !NeedsClinitCheckBeforeCall(method)) { runtime->GetInstrumentation()->UpdateMethodsCode(method, interpreter::GetNterpEntryPoint()); @@ -702,6 +703,7 @@ ClassLinker::ClassLinker(InternTable* intern_table, bool fast_class_not_found_ex quick_imt_conflict_trampoline_(nullptr), quick_generic_jni_trampoline_(nullptr), quick_to_interpreter_bridge_trampoline_(nullptr), + nterp_trampoline_(nullptr), image_pointer_size_(kRuntimePointerSize), visibly_initialized_callback_lock_("visibly initialized callback lock"), visibly_initialized_callback_(nullptr), @@ -929,6 +931,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b quick_imt_conflict_trampoline_ = GetQuickImtConflictStub(); quick_generic_jni_trampoline_ = GetQuickGenericJniStub(); quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge(); + nterp_trampoline_ = interpreter::GetNterpEntryPoint(); } // Object, String, ClassExt and DexCache need to be rerun through FindSystemClass to finish init @@ -1216,6 +1219,7 @@ struct TrampolineCheckData { const void* quick_imt_conflict_trampoline; const void* quick_generic_jni_trampoline; const void* quick_to_interpreter_bridge_trampoline; + const void* nterp_trampoline; PointerSize pointer_size; ArtMethod* m; bool error; @@ -1282,6 +1286,7 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline(); quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline(); quick_to_interpreter_bridge_trampoline_ = default_oat_header.GetQuickToInterpreterBridge(); + nterp_trampoline_ = default_oat_header.GetNterpTrampoline(); if (kIsDebugBuild) { // Check that the other images use the same trampoline. for (size_t i = 1; i < oat_files.size(); ++i) { @@ -1298,12 +1303,15 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { ith_oat_header.GetQuickGenericJniTrampoline(); const void* ith_quick_to_interpreter_bridge_trampoline = ith_oat_header.GetQuickToInterpreterBridge(); + const void* ith_nterp_trampoline = + ith_oat_header.GetNterpTrampoline(); if (ith_jni_dlsym_lookup_trampoline_ != jni_dlsym_lookup_trampoline_ || ith_jni_dlsym_lookup_critical_trampoline_ != jni_dlsym_lookup_critical_trampoline_ || ith_quick_resolution_trampoline != quick_resolution_trampoline_ || ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ || ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ || - ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_) { + ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_ || + ith_nterp_trampoline != nterp_trampoline_) { // Make sure that all methods in this image do not contain those trampolines as // entrypoints. Otherwise the class-linker won't be able to work with a single set. TrampolineCheckData data; @@ -1313,6 +1321,7 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { data.quick_imt_conflict_trampoline = ith_quick_imt_conflict_trampoline; data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline; data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline; + data.nterp_trampoline = ith_nterp_trampoline; ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { if (obj->IsClass()) { @@ -2048,8 +2057,12 @@ bool ClassLinker::AddImageSpace( } // Set image methods' entry point that point to the interpreter bridge to the // nterp entry point. - if (can_use_nterp) { - ChangeInterpreterBridgeToNterp(&method, this); + if (method.GetEntryPointFromQuickCompiledCode() == nterp_trampoline_) { + if (can_use_nterp) { + method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint()); + } else { + method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } } }, space->Begin(), image_pointer_size_); } @@ -3392,7 +3405,7 @@ const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { return GetQuickGenericJniStub(); } - if (interpreter::CanRuntimeUseNterp() && interpreter::CanMethodUseNterp(method)) { + if (interpreter::CanRuntimeUseNterp() && CanMethodUseNterp(method)) { return interpreter::GetNterpEntryPoint(); } @@ -3521,7 +3534,7 @@ void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> kla if (quick_code == nullptr && interpreter::CanRuntimeUseNterp() && - interpreter::CanMethodUseNterp(method)) { + CanMethodUseNterp(method)) { quick_code = interpreter::GetNterpEntryPoint(); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 050b02a42f..d65cde9d53 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -598,6 +598,11 @@ class ClassLinker { // Is the given entry point the JNI dlsym lookup critical stub? bool IsJniDlsymLookupCriticalStub(const void* entry_point) const; + // Is the given entry point the nterp trampoline? + bool IsNterpTrampoline(const void* entry_point) const { + return nterp_trampoline_ == entry_point; + } + const void* GetQuickToInterpreterBridgeTrampoline() const { return quick_to_interpreter_bridge_trampoline_; } @@ -1384,6 +1389,7 @@ class ClassLinker { const void* quick_imt_conflict_trampoline_; const void* quick_generic_jni_trampoline_; const void* quick_to_interpreter_bridge_trampoline_; + const void* nterp_trampoline_; // Image pointer size. PointerSize image_pointer_size_; diff --git a/runtime/image.cc b/runtime/image.cc index f5898136c0..52fc6f49c2 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -29,8 +29,8 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -// Last change: IMT index for default methods. -const uint8_t ImageHeader::kImageVersion[] = { '0', '9', '0', '\0' }; +// Last change: nterp trampoline. +const uint8_t ImageHeader::kImageVersion[] = { '0', '9', '1', '\0' }; ImageHeader::ImageHeader(uint32_t image_reservation_size, uint32_t component_count, diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc index 627aa1d00c..f45e45c8ee 100644 --- a/runtime/interpreter/mterp/nterp.cc +++ b/runtime/interpreter/mterp/nterp.cc @@ -46,18 +46,6 @@ bool CanRuntimeUseNterp() REQUIRES_SHARED(Locks::mutator_lock_) { return IsNterpSupported() && CanUseMterp() && !instr->InterpretOnly(); } -bool CanMethodUseNterp(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { - return !method->IsNative() && - method->IsInvokable() && - // Nterp supports the same methods the compiler supports. - method->IsCompilable() && - !method->MustCountLocks() && - // Proxy methods do not go through the JIT like other methods, so we don't - // run them with nterp. - !method->IsProxyMethod() && - NterpGetFrameSize(method) < kNterpMaxFrame; -} - const void* GetNterpEntryPoint() { return reinterpret_cast<const void*>(interpreter::ExecuteNterpImpl); } diff --git a/runtime/interpreter/mterp/nterp.h b/runtime/interpreter/mterp/nterp.h index fac30d90ba..97dee8c7ae 100644 --- a/runtime/interpreter/mterp/nterp.h +++ b/runtime/interpreter/mterp/nterp.h @@ -31,7 +31,6 @@ namespace interpreter { void CheckNterpAsmConstants(); bool IsNterpSupported(); bool CanRuntimeUseNterp(); -bool CanMethodUseNterp(ArtMethod* method); const void* GetNterpEntryPoint(); // The hotness threshold where we trigger JIT compilation or OSR. diff --git a/runtime/interpreter/mterp/nterp_stub.cc b/runtime/interpreter/mterp/nterp_stub.cc index c1b1ec351a..95d11c28a3 100644 --- a/runtime/interpreter/mterp/nterp_stub.cc +++ b/runtime/interpreter/mterp/nterp_stub.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "base/enums.h" #include "base/locks.h" /* @@ -34,10 +35,6 @@ bool CanRuntimeUseNterp() { return false; } -bool CanMethodUseNterp(ArtMethod* method ATTRIBUTE_UNUSED) { - return false; -} - const void* GetNterpEntryPoint() { return nullptr; } diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc index 7e76bc34f1..9670e6ed28 100644 --- a/runtime/nterp_helpers.cc +++ b/runtime/nterp_helpers.cc @@ -85,32 +85,55 @@ namespace art { static constexpr size_t kPointerSize = static_cast<size_t>(kRuntimePointerSize); -static constexpr size_t NterpGetFrameEntrySize() { - uint32_t core_spills = - RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); - uint32_t fp_spills = - RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); +static constexpr size_t NterpGetFrameEntrySize(InstructionSet isa) { + uint32_t core_spills = 0; + uint32_t fp_spills = 0; // Note: the return address is considered part of the callee saves. - return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) * kPointerSize; + switch (isa) { + case InstructionSet::kX86: + core_spills = x86::X86CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); + fp_spills = x86::X86CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); + break; + case InstructionSet::kX86_64: + core_spills = + x86_64::X86_64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); + fp_spills = x86_64::X86_64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); + break; + case InstructionSet::kArm: + case InstructionSet::kThumb2: + core_spills = arm::ArmCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); + fp_spills = arm::ArmCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); + break; + case InstructionSet::kArm64: + core_spills = arm64::Arm64CalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves); + fp_spills = arm64::Arm64CalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves); + break; + default: + InstructionSetAbort(isa); + } + // Note: the return address is considered part of the callee saves. + return (POPCOUNT(core_spills) + POPCOUNT(fp_spills)) * + static_cast<size_t>(InstructionSetPointerSize(isa)); } -size_t NterpGetFrameSize(ArtMethod* method) { +size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa) { CodeItemDataAccessor accessor(method->DexInstructionData()); const uint16_t num_regs = accessor.RegistersSize(); const uint16_t out_regs = accessor.OutsSize(); + size_t pointer_size = static_cast<size_t>(InstructionSetPointerSize(isa)); // Note: There may be two pieces of alignment but there is no need to align // out args to `kPointerSize` separately before aligning to kStackAlignment. - static_assert(IsAligned<kPointerSize>(kStackAlignment)); - static_assert(IsAligned<kPointerSize>(NterpGetFrameEntrySize())); - static_assert(IsAligned<kPointerSize>(kVRegSize * 2)); + DCHECK(IsAlignedParam(kStackAlignment, pointer_size)); + DCHECK(IsAlignedParam(NterpGetFrameEntrySize(isa), pointer_size)); + DCHECK(IsAlignedParam(kVRegSize * 2, pointer_size)); size_t frame_size = - NterpGetFrameEntrySize() + + NterpGetFrameEntrySize(isa) + (num_regs * kVRegSize) * 2 + // dex registers and reference registers - kPointerSize + // previous frame - kPointerSize + // saved dex pc + pointer_size + // previous frame + pointer_size + // saved dex pc (out_regs * kVRegSize) + // out arguments - kPointerSize; // method + pointer_size; // method return RoundUp(frame_size, kStackAlignment); } @@ -164,4 +187,16 @@ uintptr_t NterpGetCatchHandler() { return reinterpret_cast<uintptr_t>(artNterpAsmInstructionEnd); } +bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) { + return !method->IsNative() && + method->IsInvokable() && + // Nterp supports the same methods the compiler supports. + method->IsCompilable() && + !method->MustCountLocks() && + // Proxy methods do not go through the JIT like other methods, so we don't + // run them with nterp. + !method->IsProxyMethod() && + NterpGetFrameSize(method, isa) <= interpreter::kNterpMaxFrame; +} + } // namespace art diff --git a/runtime/nterp_helpers.h b/runtime/nterp_helpers.h index aacd178258..0f9e75886c 100644 --- a/runtime/nterp_helpers.h +++ b/runtime/nterp_helpers.h @@ -26,7 +26,7 @@ class ArtMethod; /** * The frame size nterp will use for the given method. */ -size_t NterpGetFrameSize(ArtMethod* method) +size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa = kRuntimeISA) REQUIRES_SHARED(Locks::mutator_lock_); /** @@ -74,6 +74,13 @@ uint32_t NterpGetVReg(ArtMethod** frame, uint16_t vreg) uint32_t NterpGetVRegReference(ArtMethod** frame, uint16_t vreg) REQUIRES_SHARED(Locks::mutator_lock_); +/** + * Returns whether the given method can run with nterp. The instruction set can + * be passed for cross-compilation. + */ +bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa = kRuntimeISA) + REQUIRES_SHARED(Locks::mutator_lock_); + } // namespace art #endif // ART_RUNTIME_NTERP_HELPERS_H_ diff --git a/runtime/oat.cc b/runtime/oat.cc index 52e0dd1dd4..321425ba00 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -77,7 +77,8 @@ OatHeader::OatHeader(InstructionSet instruction_set, quick_generic_jni_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0), quick_resolution_trampoline_offset_(0), - quick_to_interpreter_bridge_offset_(0) { + quick_to_interpreter_bridge_offset_(0), + nterp_trampoline_offset_(0) { // Don't want asserts in header as they would be checked in each file that includes it. But the // fields are private, so we check inside a method. static_assert(decltype(magic_)().size() == kOatMagic.size(), @@ -306,6 +307,24 @@ void OatHeader::SetQuickToInterpreterBridgeOffset(uint32_t offset) { quick_to_interpreter_bridge_offset_ = offset; } +const void* OatHeader::GetNterpTrampoline() const { + return GetTrampoline(*this, GetNterpTrampolineOffset()); +} + +uint32_t OatHeader::GetNterpTrampolineOffset() const { + DCHECK(IsValid()); + CHECK_GE(nterp_trampoline_offset_, quick_to_interpreter_bridge_offset_); + return nterp_trampoline_offset_; +} + +void OatHeader::SetNterpTrampolineOffset(uint32_t offset) { + CHECK(offset == 0 || offset >= quick_to_interpreter_bridge_offset_); + DCHECK(IsValid()); + DCHECK_EQ(nterp_trampoline_offset_, 0U) << offset; + + nterp_trampoline_offset_ = offset; +} + uint32_t OatHeader::GetKeyValueStoreSize() const { CHECK(IsValid()); return key_value_store_size_; diff --git a/runtime/oat.h b/runtime/oat.h index 17d3838850..0dc49c3d4d 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } }; - // Last oat version changed reason: Remove DexCache arrays. - static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '8', '9', '\0' } }; + // Last oat version changed reason: Nterp trampoline. + static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '9', '0', '\0' } }; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDebuggableKey = "debuggable"; @@ -89,6 +89,9 @@ class PACKED(4) OatHeader { const void* GetQuickToInterpreterBridge() const; uint32_t GetQuickToInterpreterBridgeOffset() const; void SetQuickToInterpreterBridgeOffset(uint32_t offset); + const void* GetNterpTrampoline() const; + uint32_t GetNterpTrampolineOffset() const; + void SetNterpTrampolineOffset(uint32_t offset); InstructionSet GetInstructionSet() const; uint32_t GetInstructionSetFeaturesBitmap() const; @@ -133,6 +136,7 @@ class PACKED(4) OatHeader { uint32_t quick_imt_conflict_trampoline_offset_; uint32_t quick_resolution_trampoline_offset_; uint32_t quick_to_interpreter_bridge_offset_; + uint32_t nterp_trampoline_offset_; uint32_t key_value_store_size_; uint8_t key_value_store_[0]; // note variable width data at end |