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
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 331b8b7..795d621 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3355,6 +3355,11 @@
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 f9cd8c8..72949b0 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -104,9 +104,9 @@
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 @@
// 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 5d90b9c..844a0ff 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -441,6 +441,9 @@
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 @@
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 ac6988a..d3005cd 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -250,6 +250,17 @@
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 3f0de3b..7f2d926 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3751,6 +3751,10 @@
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 4364df0..8ada63c 100644
--- a/runtime/interpreter/mterp/arm64ng/main.S
+++ b/runtime/interpreter/mterp/arm64ng/main.S
@@ -308,12 +308,7 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
// 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 7fe507c..5a086f5 100644
--- a/runtime/interpreter/mterp/armng/main.S
+++ b/runtime/interpreter/mterp/armng/main.S
@@ -316,12 +316,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 c58c8a0..d70a846 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -693,7 +693,12 @@
// 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 @@
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 bfbd398..bd191c0 100644
--- a/runtime/interpreter/mterp/x86_64ng/main.S
+++ b/runtime/interpreter/mterp/x86_64ng/main.S
@@ -255,7 +255,7 @@
// 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 @@
// 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 @@
#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 @@
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 @@
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 @@
// 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 744d9ce..1077bcf 100644
--- a/runtime/interpreter/mterp/x86ng/main.S
+++ b/runtime/interpreter/mterp/x86ng/main.S
@@ -312,12 +312,7 @@
// 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 @@
#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 @@
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 @@
NterpGetInstanceField:
OP_IGET load="movl", wide=0
-NterpCallSuspend:
+NterpCallSuspendAndGotoNext:
EXPORT_PC
// Save branch offset.
movl rINST, LOCAL0(%esp)
@@ -2241,18 +2257,21 @@
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 @@
// 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 32465f2..237f63c 100644
--- a/runtime/jit/jit-inl.h
+++ b/runtime/jit/jit-inl.h
@@ -28,12 +28,17 @@
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 549718d..c9e6bbf 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -1728,7 +1728,7 @@
}
}
-void Jit::EnqueueCompilation(ArtMethod* method, Thread* self) {
+void Jit::MaybeEnqueueCompilation(ArtMethod* method, Thread* self) {
if (thread_pool_ == nullptr) {
return;
}
@@ -1742,6 +1742,10 @@
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 @@
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 8c1b688..b439c8e 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -438,7 +438,7 @@
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 @@
// 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 0fe6c4c..dd8b061 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -188,6 +188,8 @@
// 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 @@
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 @@
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 @@
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 @@
// 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 a73bbed..d5ba599 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 7df7650..bae9200 100644
--- a/tools/cpp-define-generator/thread.def
+++ b/tools/cpp-define-generator/thread.def
@@ -67,3 +67,5 @@
.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())