diff options
author | 2022-02-15 22:54:11 +0000 | |
---|---|---|
committer | 2022-03-18 14:42:59 +0000 | |
commit | f9ae8e35fe913bbe31fa53f00a8c4f0e071b25de (patch) | |
tree | ae12648fdf37f9da5211724a1dd0deff28b1c802 | |
parent | 3dd6219114a05134d9b670053279c084fb19fb74 (diff) |
Implement shared counters for boot image / zygote methods.
Add a thread-local counter which the interpreter increments. When
hitting zero, the interpreter goes into the runtime and updates a map of
counters. If the counter for the particular method hits a threshold, we
JIT it.
Test: test.py
Test: imgdiag looking at boot.art
Bug: 162110941
Change-Id: I6493332eafc51856494ef20592ca83bc716c994e
-rw-r--r-- | dex2oat/linker/image_writer.cc | 5 | ||||
-rw-r--r-- | libdexfile/dex/modifiers.h | 8 | ||||
-rw-r--r-- | runtime/art_method-inl.h | 6 | ||||
-rw-r--r-- | runtime/art_method.h | 11 | ||||
-rw-r--r-- | runtime/class_linker.cc | 4 | ||||
-rw-r--r-- | runtime/interpreter/mterp/arm64ng/main.S | 56 | ||||
-rw-r--r-- | runtime/interpreter/mterp/armng/main.S | 61 | ||||
-rw-r--r-- | runtime/interpreter/mterp/nterp.cc | 9 | ||||
-rw-r--r-- | runtime/interpreter/mterp/x86_64ng/main.S | 52 | ||||
-rw-r--r-- | runtime/interpreter/mterp/x86ng/main.S | 52 | ||||
-rw-r--r-- | runtime/jit/jit-inl.h | 15 | ||||
-rw-r--r-- | runtime/jit/jit.cc | 22 | ||||
-rw-r--r-- | runtime/jit/jit.h | 6 | ||||
-rw-r--r-- | runtime/thread.h | 34 | ||||
-rw-r--r-- | tools/cpp-define-generator/art_method.def | 4 | ||||
-rw-r--r-- | tools/cpp-define-generator/thread.def | 2 |
16 files changed, 260 insertions, 87 deletions
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 331b8b7c5e..795d6214f9 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -3355,6 +3355,11 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, nullptr, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); } + if (!orig->IsRuntimeMethod() && + (compiler_options_.IsBootImage() || compiler_options_.IsBootImageExtension())) { + orig->SetMemorySharedMethod(); + } + memcpy(copy, orig, ArtMethod::Size(target_ptr_size_)); CopyAndFixupReference(copy->GetDeclaringClassAddressWithoutBarrier(), diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h index f9cd8c8c76..72949b0b7b 100644 --- a/libdexfile/dex/modifiers.h +++ b/libdexfile/dex/modifiers.h @@ -104,9 +104,9 @@ static constexpr uint32_t kAccNterpInvokeFastPathFlag = 0x00200000; // meth static constexpr uint32_t kAccPublicApi = 0x10000000; // field, method static constexpr uint32_t kAccCorePlatformApi = 0x20000000; // field, method -// Not currently used, except for intrinsic methods where these bits -// are part of the intrinsic ordinal. -static constexpr uint32_t kAccMayBeUnusedBits = 0x40000000; +// For methods which we'd like to share memory between zygote and apps. +// Uses an intrinsic bit but that's OK as intrinsics are always in the boot image. +static constexpr uint32_t kAccMemorySharedMethod = 0x40000000; // Set by the compiler driver when compiling boot classes with instrinsic methods. static constexpr uint32_t kAccIntrinsic = 0x80000000; // method (runtime) @@ -125,7 +125,7 @@ static constexpr uint32_t kAccHiddenapiBits = kAccPublicApi | kAccCorePlatformAp // which overlap are not valid when kAccIntrinsic is set. static constexpr uint32_t kAccIntrinsicBits = kAccHiddenapiBits | kAccSingleImplementation | kAccMustCountLocks | kAccCompileDontBother | kAccCopied | - kAccPreviouslyWarm | kAccMayBeUnusedBits; + kAccPreviouslyWarm | kAccMemorySharedMethod; // Valid (meaningful) bits for a field. static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected | diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 5d90b9c054..844a0ffa9b 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -441,6 +441,9 @@ inline void ArtMethod::ResetCounter(uint16_t new_value) { if (IsAbstract()) { return; } + if (IsMemorySharedMethod()) { + return; + } DCHECK_EQ(new_value, Runtime::Current()->GetJITOptions()->GetWarmupThreshold()); // Avoid dirtying the value if possible. if (hotness_count_ != new_value) { @@ -460,6 +463,9 @@ inline void ArtMethod::UpdateCounter(int32_t new_samples) { DCHECK(!IsAbstract()); DCHECK_GT(new_samples, 0); DCHECK_LE(new_samples, std::numeric_limits<uint16_t>::max()); + if (IsMemorySharedMethod()) { + return; + } uint16_t old_hotness_count = hotness_count_; uint16_t new_count = (old_hotness_count <= new_samples) ? 0u : old_hotness_count - new_samples; // Avoid dirtying the value if possible. diff --git a/runtime/art_method.h b/runtime/art_method.h index ac6988aa76..d3005cddf8 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -250,6 +250,17 @@ class ArtMethod final { AddAccessFlags(kAccPreCompiled | kAccCompileDontBother); } + bool IsMemorySharedMethod() { + return (GetAccessFlags() & kAccMemorySharedMethod) != 0; + } + + void SetMemorySharedMethod() REQUIRES_SHARED(Locks::mutator_lock_) { + if (!IsIntrinsic() && !IsAbstract()) { + AddAccessFlags(kAccMemorySharedMethod); + SetHotCounter(); + } + } + void ClearPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) { ClearAccessFlags(kAccPreCompiled | kAccCompileDontBother); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3f0de3b7c7..7f2d92686c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3751,6 +3751,10 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, std::none_of(shorty.begin() + slow_args_search_start, shorty.end(), is_slow_arg))) { dst->SetNterpInvokeFastPathFlag(); } + + if (Runtime::Current()->IsZygote()) { + dst->SetMemorySharedMethod(); + } } void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile* dex_file) { diff --git a/runtime/interpreter/mterp/arm64ng/main.S b/runtime/interpreter/mterp/arm64ng/main.S index 4364df00a1..8ada63c03e 100644 --- a/runtime/interpreter/mterp/arm64ng/main.S +++ b/runtime/interpreter/mterp/arm64ng/main.S @@ -308,12 +308,7 @@ END \name cbz w2, NterpHandleHotnessOverflow add x2, x2, #-1 strh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET] - // Otherwise, do a suspend check. - ldr w0, [xSELF, #THREAD_FLAGS_OFFSET] - tst w0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST - b.eq 1b - EXPORT_PC - bl art_quick_test_suspend + DO_SUSPEND_CHECK continue_label=1b b 1b .endm @@ -432,25 +427,22 @@ END \name #error Expected 0 for hotness value #endif // If the counter is at zero, handle this in the runtime. - cbz w2, 2f + cbz w2, 3f add x2, x2, #-1 strh w2, [x0, #ART_METHOD_HOTNESS_COUNT_OFFSET] - ldr w0, [xSELF, #THREAD_FLAGS_OFFSET] - tst w0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST - b.ne 3f 1: + DO_SUSPEND_CHECK continue_label=2f +2: FETCH_INST GET_INST_OPCODE ip GOTO_OPCODE ip -2: +3: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b +4: mov x1, xzr mov x2, xFP bl nterp_hot_method - b 1b -3: - EXPORT_PC - bl art_quick_test_suspend - b 1b + b 2b .endm .macro SPILL_ALL_CALLEE_SAVES @@ -1571,6 +1563,24 @@ END \name cbnz \ins, 1b .endm +.macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot + ldr wip, [x0, #ART_METHOD_ACCESS_FLAGS_OFFSET] + tbz wip, #ART_METHOD_IS_MEMORY_SHARED_FLAG_BIT, \if_hot + ldr wip, [xSELF, #THREAD_SHARED_METHOD_HOTNESS_OFFSET] + cbz wip, \if_hot + add wip, wip, #-1 + str wip, [xSELF, #THREAD_SHARED_METHOD_HOTNESS_OFFSET] + b \if_not_hot +.endm + +.macro DO_SUSPEND_CHECK continue_label + ldr wip, [xSELF, #THREAD_FLAGS_OFFSET] + tst wip, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST + b.eq \continue_label + EXPORT_PC + bl art_quick_test_suspend +.endm + %def entry(): /* * ArtMethod entry point. @@ -1754,14 +1764,17 @@ NterpHandleStringInitRange: COMMON_INVOKE_RANGE is_string_init=1, suffix="stringInit" NterpHandleHotnessOverflow: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=5f +1: mov x1, xPC mov x2, xFP bl nterp_hot_method - cbnz x0, 1f + cbnz x0, 3f +2: FETCH wINST, 0 // load wINST GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -1: +3: // Drop the current frame. ldr ip, [xREFS, #-8] mov sp, ip @@ -1801,11 +1814,11 @@ NterpHandleHotnessOverflow: sub sp, sp, x1 add x2, x0, #OSR_DATA_MEMORY -2: +4: sub x1, x1, #8 ldr ip, [x2, x1] str ip, [sp, x1] - cbnz x1, 2b + cbnz x1, 4b // Fetch the native PC to jump to and save it in a callee-save register. ldr xFP, [x0, #OSR_DATA_NATIVE_PC] @@ -1815,6 +1828,9 @@ NterpHandleHotnessOverflow: // Jump to the compiled code. br xFP +5: + DO_SUSPEND_CHECK continue_label=2b + b 2b // This is the logical end of ExecuteNterpImpl, where the frame info applies. // EndExecuteNterpImpl includes the methods below as we want the runtime to diff --git a/runtime/interpreter/mterp/armng/main.S b/runtime/interpreter/mterp/armng/main.S index 7fe507cf69..5a086f597d 100644 --- a/runtime/interpreter/mterp/armng/main.S +++ b/runtime/interpreter/mterp/armng/main.S @@ -316,12 +316,7 @@ END \name beq NterpHandleHotnessOverflow add r2, r2, #-1 strh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET] - // Otherwise, do a suspend check. - ldr r0, [rSELF, #THREAD_FLAGS_OFFSET] - tst r0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST - beq 1b - EXPORT_PC - bl art_quick_test_suspend + DO_SUSPEND_CHECK continue_label=1b b 1b .endm @@ -455,25 +450,22 @@ END \name ldr r0, [sp] ldrh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET] cmp r2, #NTERP_HOTNESS_VALUE - beq 2f + beq 3f add r2, r2, #-1 strh r2, [r0, #ART_METHOD_HOTNESS_COUNT_OFFSET] - ldr r0, [rSELF, #THREAD_FLAGS_OFFSET] - tst r0, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST - bne 3f 1: + DO_SUSPEND_CHECK continue_label=2f +2: FETCH_INST GET_INST_OPCODE ip GOTO_OPCODE ip -2: +3: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b +4: mov r1, #0 mov r2, rFP bl nterp_hot_method - b 1b -3: - EXPORT_PC - bl art_quick_test_suspend - b 1b + b 2b .endm .macro SPILL_ALL_CALLEE_SAVES @@ -1584,6 +1576,28 @@ END \name bne 1b .endm +.macro DO_SUSPEND_CHECK continue_label + // Otherwise, do a suspend check. + ldr ip, [rSELF, #THREAD_FLAGS_OFFSET] + tst ip, #THREAD_SUSPEND_OR_CHECKPOINT_REQUEST + beq \continue_label + EXPORT_PC + bl art_quick_test_suspend +.endm + +.macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot + ldr ip, [r0, #ART_METHOD_ACCESS_FLAGS_OFFSET] + tst ip, #ART_METHOD_IS_MEMORY_SHARED_FLAG + beq \if_hot + ldr ip, [rSELF, #THREAD_SHARED_METHOD_HOTNESS_OFFSET] + cmp ip, #0 + beq \if_hot + add ip, ip, #-1 + str ip, [rSELF, #THREAD_SHARED_METHOD_HOTNESS_OFFSET] + b \if_not_hot +.endm + + %def entry(): /* * ArtMethod entry point. @@ -1770,15 +1784,18 @@ NterpHandleStringInitRange: NterpHandleHotnessOverflow: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=5f +1: mov r1, rPC mov r2, rFP bl nterp_hot_method cmp r0, #0 - bne 1f + bne 3f +2: FETCH_INST // load rINST GET_INST_OPCODE ip // extract opcode from rINST GOTO_OPCODE ip // jump to next instruction -1: +3: // Drop the current frame. ldr ip, [rREFS, #-4] mov sp, ip @@ -1815,12 +1832,12 @@ NterpHandleHotnessOverflow: sub sp, sp, r1 add r2, r0, #OSR_DATA_MEMORY -2: +4: sub r1, r1, #4 ldr ip, [r2, r1] str ip, [sp, r1] cmp r1, #0 - bne 2b + bne 4b // Fetch the native PC to jump to and save it in a callee-save register. ldr rFP, [r0, #OSR_DATA_NATIVE_PC] @@ -1830,6 +1847,10 @@ NterpHandleHotnessOverflow: // Jump to the compiled code. bx rFP +5: + DO_SUSPEND_CHECK continue_label=2b + b 2b + // This is the logical end of ExecuteNterpImpl, where the frame info applies. // EndExecuteNterpImpl includes the methods below as we want the runtime to // see them as part of the Nterp PCs. diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc index c58c8a0e3b..d70a846b7c 100644 --- a/runtime/interpreter/mterp/nterp.cc +++ b/runtime/interpreter/mterp/nterp.cc @@ -693,7 +693,12 @@ extern "C" jit::OsrData* NterpHotMethod(ArtMethod* method, uint16_t* dex_pc_ptr, // method. ScopedAssertNoThreadSuspension sants("In nterp"); Runtime* runtime = Runtime::Current(); - method->ResetCounter(runtime->GetJITOptions()->GetWarmupThreshold()); + if (method->IsMemorySharedMethod()) { + DCHECK_EQ(Thread::Current()->GetSharedMethodHotness(), 0u); + Thread::Current()->ResetSharedMethodHotness(); + } else { + method->ResetCounter(runtime->GetJITOptions()->GetWarmupThreshold()); + } jit::Jit* jit = runtime->GetJit(); if (jit != nullptr && jit->UseJitCompilation()) { // Nterp passes null on entry where we don't want to OSR. @@ -707,7 +712,7 @@ extern "C" jit::OsrData* NterpHotMethod(ArtMethod* method, uint16_t* dex_pc_ptr, return osr_data; } } - jit->EnqueueCompilation(method, Thread::Current()); + jit->MaybeEnqueueCompilation(method, Thread::Current()); } return nullptr; } diff --git a/runtime/interpreter/mterp/x86_64ng/main.S b/runtime/interpreter/mterp/x86_64ng/main.S index bfbd398934..bd191c09ec 100644 --- a/runtime/interpreter/mterp/x86_64ng/main.S +++ b/runtime/interpreter/mterp/x86_64ng/main.S @@ -255,7 +255,7 @@ END_FUNCTION \name // Update method counter and do a suspend check if the branch is negative or zero. testq rINSTq, rINSTq jle 3f -2: +2: // We use 2 and not 1 for this local label as the users of the BRANCH macro have a 1 label. FETCH_INST GOTO_NEXT 3: @@ -270,11 +270,7 @@ END_FUNCTION \name // Update counter. addl $$-1, %esi movw %si, ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi) - // Otherwise, do a suspend check. - testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET - jz 2b - EXPORT_PC - call SYMBOL(art_quick_test_suspend) + DO_SUSPEND_CHECK continue_label=2b jmp 2b .endm @@ -831,23 +827,23 @@ END_FUNCTION \name #error Expected 0 for hotness value #endif // If the counter is at zero, handle this in the runtime. - testw %si, %si - je 2f + testl %esi, %esi + je 3f // Update counter. addl $$-1, %esi movw %si, ART_METHOD_HOTNESS_COUNT_OFFSET(%rdi) - testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET - jz 1f - EXPORT_PC - call SYMBOL(art_quick_test_suspend) 1: + DO_SUSPEND_CHECK continue_label=2f +2: FETCH_INST GOTO_NEXT -2: +3: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b +4: movq $$0, %rsi movq rFP, %rdx call nterp_hot_method - jmp 1b + jmp 2b .endm .macro SPILL_ALL_CALLEE_SAVES @@ -1671,6 +1667,24 @@ END_FUNCTION \name jne 1b .endm +.macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot + testl $$ART_METHOD_IS_MEMORY_SHARED_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%rdi) + jz \if_hot + movzwl rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET, %esi + testl %esi, %esi + je \if_hot + addl $$-1, %esi + movw %si, rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET + jmp \if_not_hot +.endm + +.macro DO_SUSPEND_CHECK continue_label + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET + jz \continue_label + EXPORT_PC + call SYMBOL(art_quick_test_suspend) +.endm + %def entry(): /* * ArtMethod entry point. @@ -2168,14 +2182,17 @@ NterpGetInstanceField: OP_IGET load="movl", wide=0 NterpHandleHotnessOverflow: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=4f +1: movq rPC, %rsi movq rFP, %rdx call nterp_hot_method testq %rax, %rax - jne 1f + jne 3f +2: FETCH_INST GOTO_NEXT -1: +3: // Drop the current frame. movq -8(rREFS), %rsp CFI_DEF_CFA(rsp, CALLEE_SAVES_SIZE) @@ -2203,6 +2220,9 @@ NterpHandleHotnessOverflow: // Jump to the compiled code. jmp *%rbx +4: + DO_SUSPEND_CHECK continue_label=2b + jmp 2b NterpHandleInvokeInterfaceOnObjectMethodRange: shrl $$16, %eax diff --git a/runtime/interpreter/mterp/x86ng/main.S b/runtime/interpreter/mterp/x86ng/main.S index 744d9ce281..1077bcf20e 100644 --- a/runtime/interpreter/mterp/x86ng/main.S +++ b/runtime/interpreter/mterp/x86ng/main.S @@ -312,12 +312,7 @@ END_FUNCTION \name // Update counter. addl $$-1, %ecx movw %cx, ART_METHOD_HOTNESS_COUNT_OFFSET(%eax) - // If the counter overflows, handle this in the runtime. - jz NterpHandleHotnessOverflow - // Otherwise, do a suspend check. - testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET - jz 2b - jmp NterpCallSuspend + DO_SUSPEND_CHECK continue_label=2b .endm // Expects: @@ -978,25 +973,28 @@ END_FUNCTION \name #error Expected 0 for hotness value #endif // If the counter is at zero, handle this in the runtime. - testw %cx, %cx + testl %ecx, %ecx je 2f // Update counter. addl $$-1, %ecx movw %cx, ART_METHOD_HOTNESS_COUNT_OFFSET(%eax) - jz 2f + jz 3f +1: testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET - jz 1f + jz 2f EXPORT_PC call SYMBOL(art_quick_test_suspend) RESTORE_IBASE -1: +2: FETCH_INST GOTO_NEXT -2: +3: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=4f, if_not_hot=1b +4: movl $$0, ARG1 movl rFP, ARG2 call nterp_hot_method - jmp 1b + jmp 2b .endm .macro SPILL_ALL_CALLEE_SAVES @@ -1733,6 +1731,24 @@ END_FUNCTION \name jne 1b .endm +.macro DO_SUSPEND_CHECK continue_label + testl $$(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST), rSELF:THREAD_FLAGS_OFFSET + jz \continue_label + jmp NterpCallSuspendAndGotoNext +.endm + +.macro CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot, if_not_hot + testl $$ART_METHOD_IS_MEMORY_SHARED_FLAG, ART_METHOD_ACCESS_FLAGS_OFFSET(%eax) + jz \if_hot + movzwl rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET, %ecx + testl %ecx, %ecx + je \if_hot + addl $$-1, %ecx + movw %cx, rSELF:THREAD_SHARED_METHOD_HOTNESS_OFFSET + jmp \if_not_hot +.endm + + %def entry(): /* * ArtMethod entry point. @@ -2230,7 +2246,7 @@ NterpGetWideInstanceField: NterpGetInstanceField: OP_IGET load="movl", wide=0 -NterpCallSuspend: +NterpCallSuspendAndGotoNext: EXPORT_PC // Save branch offset. movl rINST, LOCAL0(%esp) @@ -2241,18 +2257,21 @@ NterpCallSuspend: GOTO_NEXT NterpHandleHotnessOverflow: + CHECK_AND_UPDATE_SHARED_MEMORY_METHOD if_hot=1f, if_not_hot=4f +1: movl rPC, %ecx movl rFP, ARG2 // Save next PC. movl %ecx, LOCAL0(%esp) call nterp_hot_method testl %eax, %eax - jne 1f + jne 3f // Fetch next PC. mov LOCAL0(%esp), rPC +2: FETCH_INST GOTO_NEXT -1: +3: // Drop the current frame. movl -4(rREFS), %esp CFI_DEF_CFA(esp, PARAMETERS_SAVES_SIZE+CALLEE_SAVES_SIZE) @@ -2288,6 +2307,9 @@ NterpHandleHotnessOverflow: // Jump to the compiled code. ret +4: + DO_SUSPEND_CHECK continue_label=2b + NterpHandleInvokeInterfaceOnObjectMethodRange: shrl $$16, %eax diff --git a/runtime/jit/jit-inl.h b/runtime/jit/jit-inl.h index 32465f28fe..237f63ce97 100644 --- a/runtime/jit/jit-inl.h +++ b/runtime/jit/jit-inl.h @@ -28,12 +28,17 @@ namespace art { namespace jit { inline void Jit::AddSamples(Thread* self, ArtMethod* method) { - if (IgnoreSamplesForMethod(method)) { - return; - } if (method->CounterIsHot()) { - method->ResetCounter(Runtime::Current()->GetJITOptions()->GetWarmupThreshold()); - EnqueueCompilation(method, self); + if (method->IsMemorySharedMethod()) { + if (self->DecrementSharedMethodHotness() == 0) { + self->ResetSharedMethodHotness(); + } else { + return; + } + } else { + method->ResetCounter(Runtime::Current()->GetJITOptions()->GetWarmupThreshold()); + } + MaybeEnqueueCompilation(method, self); } else { method->UpdateCounter(1); } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 549718dcd6..c9e6bbfa53 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -1728,7 +1728,7 @@ bool Jit::CanAssumeInitialized(ObjPtr<mirror::Class> cls, bool is_for_shared_reg } } -void Jit::EnqueueCompilation(ArtMethod* method, Thread* self) { +void Jit::MaybeEnqueueCompilation(ArtMethod* method, Thread* self) { if (thread_pool_ == nullptr) { return; } @@ -1742,6 +1742,10 @@ void Jit::EnqueueCompilation(ArtMethod* method, Thread* self) { return; } + if (IgnoreSamplesForMethod(method)) { + return; + } + if (GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { if (!method->IsNative() && !code_cache_->IsOsrCompiled(method)) { // If we already have compiled code for it, nterp may be stuck in a loop. @@ -1765,8 +1769,20 @@ void Jit::EnqueueCompilation(ArtMethod* method, Thread* self) { return; } - if (IgnoreSamplesForMethod(method)) { - return; + static constexpr size_t kIndividualSharedMethodHotnessThreshold = 0xff; + if (method->IsMemorySharedMethod()) { + MutexLock mu(self, lock_); + auto it = shared_method_counters_.find(method); + if (it == shared_method_counters_.end()) { + shared_method_counters_[method] = kIndividualSharedMethodHotnessThreshold; + return; + } else if (it->second != 0) { + DCHECK_LE(it->second, kIndividualSharedMethodHotnessThreshold); + shared_method_counters_[method] = it->second - 1; + return; + } else { + shared_method_counters_[method] = kIndividualSharedMethodHotnessThreshold; + } } if (!method->IsNative() && GetCodeCache()->CanAllocateProfilingInfo()) { diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 8c1b6888af..b439c8ee9e 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -438,7 +438,7 @@ class Jit { void EnqueueOptimizedCompilation(ArtMethod* method, Thread* self); - void EnqueueCompilation(ArtMethod* method, Thread* self) + void MaybeEnqueueCompilation(ArtMethod* method, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); private: @@ -503,6 +503,10 @@ class Jit { // recomputing it. size_t fd_methods_size_; + // Map of hotness counters for methods which we want to share the memory + // between the zygote and apps. + std::map<ArtMethod*, uint16_t> shared_method_counters_; + DISALLOW_COPY_AND_ASSIGN(Jit); }; diff --git a/runtime/thread.h b/runtime/thread.h index 0fe6c4c1f1..dd8b061b95 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -188,6 +188,8 @@ enum class WeakRefAccessState : int32_t { // This should match RosAlloc::kNumThreadLocalSizeBrackets. static constexpr size_t kNumRosAllocThreadLocalSizeBracketsInThread = 16; +static constexpr size_t kSharedMethodHotnessThreshold = 0xffff; + // Thread's stack layout for implicit stack overflow checks: // // +---------------------+ <- highest address of stack memory @@ -768,6 +770,13 @@ class Thread { return sizeof(tls32_.is_gc_marking); } + template<PointerSize pointer_size> + static constexpr ThreadOffset<pointer_size> SharedMethodHotnessOffset() { + return ThreadOffset<pointer_size>( + OFFSETOF_MEMBER(Thread, tls32_) + + OFFSETOF_MEMBER(tls_32bit_sized_values, shared_method_hotness)); + } + // Deoptimize the Java stack. void DeoptimizeWithDeoptimizationException(JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); @@ -1388,6 +1397,19 @@ class Thread { return StateAndFlags::EncodeState(state); } + void ResetSharedMethodHotness() { + tls32_.shared_method_hotness = kSharedMethodHotnessThreshold; + } + + uint32_t GetSharedMethodHotness() const { + return tls32_.shared_method_hotness; + } + + uint32_t DecrementSharedMethodHotness() { + tls32_.shared_method_hotness = (tls32_.shared_method_hotness - 1) & 0xffff; + return tls32_.shared_method_hotness; + } + private: explicit Thread(bool daemon); ~Thread() REQUIRES(!Locks::mutator_lock_, !Locks::thread_suspend_count_lock_); @@ -1696,7 +1718,9 @@ class Thread { force_interpreter_count(0), make_visibly_initialized_counter(0), define_class_counter(0), - num_name_readers(0) {} + num_name_readers(0), + shared_method_hotness(kSharedMethodHotnessThreshold) + {} // The state and flags field must be changed atomically so that flag values aren't lost. // See `StateAndFlags` for bit assignments of `ThreadFlag` and `ThreadState` values. @@ -1790,6 +1814,14 @@ class Thread { // retrieved. mutable std::atomic<uint32_t> num_name_readers; static_assert(std::atomic<uint32_t>::is_always_lock_free); + + // Thread-local hotness counter for shared memory methods. Initialized with + // `kSharedMethodHotnessThreshold`. The interpreter decrements it and goes + // into the runtime when hitting zero. Note that all previous decrements + // could have been executed by another method than the one seeing zero. + // There is a second level counter in `Jit::shared_method_counters_` to make + // sure we at least have a few samples before compiling a method. + uint32_t shared_method_hotness; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/tools/cpp-define-generator/art_method.def b/tools/cpp-define-generator/art_method.def index a73bbedc8a..d5ba59998d 100644 --- a/tools/cpp-define-generator/art_method.def +++ b/tools/cpp-define-generator/art_method.def @@ -21,6 +21,10 @@ ASM_DEFINE(ART_METHOD_ACCESS_FLAGS_OFFSET, art::ArtMethod::AccessFlagsOffset().Int32Value()) +ASM_DEFINE(ART_METHOD_IS_MEMORY_SHARED_FLAG, + art::kAccMemorySharedMethod) +ASM_DEFINE(ART_METHOD_IS_MEMORY_SHARED_FLAG_BIT, + art::MostSignificantBit(art::kAccMemorySharedMethod)) ASM_DEFINE(ART_METHOD_IS_STATIC_FLAG, art::kAccStatic) ASM_DEFINE(ART_METHOD_IS_STATIC_FLAG_BIT, diff --git a/tools/cpp-define-generator/thread.def b/tools/cpp-define-generator/thread.def index 7df7650890..bae92009b2 100644 --- a/tools/cpp-define-generator/thread.def +++ b/tools/cpp-define-generator/thread.def @@ -67,3 +67,5 @@ ASM_DEFINE(THREAD_ALLOC_ARRAY_ENTRYPOINT_OFFSET, .Int32Value()) ASM_DEFINE(THREAD_READ_BARRIER_MARK_REG00_OFFSET, art::Thread::ReadBarrierMarkEntryPointsOffset<art::kRuntimePointerSize>(0)) +ASM_DEFINE(THREAD_SHARED_METHOD_HOTNESS_OFFSET, + art::Thread::SharedMethodHotnessOffset<art::kRuntimePointerSize>().Int32Value()) |