summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2022-02-15 22:54:11 +0000
committer Nicolas Geoffray <ngeoffray@google.com> 2022-03-18 14:42:59 +0000
commitf9ae8e35fe913bbe31fa53f00a8c4f0e071b25de (patch)
treeae12648fdf37f9da5211724a1dd0deff28b1c802
parent3dd6219114a05134d9b670053279c084fb19fb74 (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.cc5
-rw-r--r--libdexfile/dex/modifiers.h8
-rw-r--r--runtime/art_method-inl.h6
-rw-r--r--runtime/art_method.h11
-rw-r--r--runtime/class_linker.cc4
-rw-r--r--runtime/interpreter/mterp/arm64ng/main.S56
-rw-r--r--runtime/interpreter/mterp/armng/main.S61
-rw-r--r--runtime/interpreter/mterp/nterp.cc9
-rw-r--r--runtime/interpreter/mterp/x86_64ng/main.S52
-rw-r--r--runtime/interpreter/mterp/x86ng/main.S52
-rw-r--r--runtime/jit/jit-inl.h15
-rw-r--r--runtime/jit/jit.cc22
-rw-r--r--runtime/jit/jit.h6
-rw-r--r--runtime/thread.h34
-rw-r--r--tools/cpp-define-generator/art_method.def4
-rw-r--r--tools/cpp-define-generator/thread.def2
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())