Boot image: only use the resolution stub when there is AOT code.
This avoids dirtying the page when the class becomes initialized.
Test: test.py
Bug: 119800099
Change-Id: I5cd7ae56b6637583179dbb766da251977b7ac961
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index e1839fe..dd29fa2 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3371,12 +3371,9 @@
quick_code = method->IsNative()
? GetOatAddress(StubType::kQuickGenericJNITrampoline)
: GetOatAddress(StubType::kQuickToInterpreterBridge);
- }
-
- if (!method->GetDeclaringClass()->IsInitialized() &&
- method->IsStatic() &&
- !method->IsConstructor()) {
- // We need to go through the resolution stub for class initialization.
+ } else if (method->NeedsInitializationCheck()) {
+ // If we do have code and the method needs an initialization check, go through
+ // the resolution stub.
quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
}
return quick_code;
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 646f73d..6cfb015 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -881,4 +881,8 @@
method->GetDeclaringClass<kReadBarrierOption>()->IsErroneous());
}
+bool ArtMethod::NeedsInitializationCheck() {
+ return IsStatic() && !IsConstructor() && !GetDeclaringClass()->IsVisiblyInitialized();
+}
+
} // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d84ea7c..46cc1ed 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -365,6 +365,10 @@
AddAccessFlags(kAccMustCountLocks);
}
+ // Returns whether the method can be invoked directly or needs a
+ // class initialization check.
+ bool NeedsInitializationCheck() REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 93fb8a3..a1372ec 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3725,6 +3725,10 @@
// Only update static methods.
continue;
}
+ if (!IsQuickResolutionStub(method->GetEntryPointFromQuickCompiledCode())) {
+ // Only update methods whose entrypoint is the resolution stub.
+ continue;
+ }
const void* quick_code = nullptr;
if (has_oat_class) {
OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index d0901c4..58f263e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2381,6 +2381,21 @@
jit->MethodEntered(self, called);
}
+ // We can set the entrypoint of a native method to generic JNI even when the
+ // class hasn't been initialized, so we need to do the initialization check
+ // before invoking the native code.
+ if (called->NeedsInitializationCheck()) {
+ // Ensure static method's class is initialized.
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(called->GetDeclaringClass()));
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+ DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
+ self->PopHandleScope();
+ // A negative value denotes an error.
+ return GetTwoWordFailureValue();
+ }
+ }
+
uint32_t cookie;
uint32_t* sp32;
// Skip calling JniMethodStart for @CriticalNative.
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 08883fc..d6d0b87 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -1023,12 +1023,15 @@
return false;
}
if (UNLIKELY(method->IsPreCompiled()) && !with_backedges /* don't check for OSR */) {
- const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method);
- if (entry_point != nullptr) {
- Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point);
- return true;
+ if (!method->NeedsInitializationCheck()) {
+ const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method);
+ if (entry_point != nullptr) {
+ Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point);
+ return true;
+ }
}
}
+
if (IgnoreSamplesForMethod(method)) {
return false;
}
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index ecfe9b6..665b8be 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -757,7 +757,7 @@
data->SetCode(code_ptr);
instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation();
for (ArtMethod* m : data->GetMethods()) {
- if (!class_linker->IsQuickResolutionStub(m->GetEntryPointFromQuickCompiledCode())) {
+ if (!m->NeedsInitializationCheck()) {
instrum->UpdateMethodsCode(m, method_header->GetEntryPoint());
}
}
@@ -770,8 +770,7 @@
if (osr) {
number_of_osr_compilations_++;
osr_code_map_.Put(method, code_ptr);
- } else if (class_linker->IsQuickResolutionStub(
- method->GetEntryPointFromQuickCompiledCode())) {
+ } else if (method->NeedsInitializationCheck()) {
// This situation currently only occurs in the jit-zygote mode.
DCHECK(Runtime::Current()->IsUsingApexBootImageLocation());
DCHECK(!garbage_collect_code_);
@@ -1572,18 +1571,15 @@
return false;
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsQuickResolutionStub(method->GetEntryPointFromQuickCompiledCode())) {
- if (!prejit) {
- // Unless we're pre-jitting, we currently don't save the JIT compiled code if we cannot
- // update the entrypoint due to having the resolution stub.
- VLOG(jit) << "Not compiling "
- << method->PrettyMethod()
- << " because it has the resolution stub";
- // Give it a new chance to be hot.
- ClearMethodCounter(method, /*was_warm=*/ false);
- return false;
- }
+ if (method->NeedsInitializationCheck() && !prejit) {
+ // Unless we're pre-jitting, we currently don't save the JIT compiled code if we cannot
+ // update the entrypoint due to needing an initialization check.
+ VLOG(jit) << "Not compiling "
+ << method->PrettyMethod()
+ << " because it has the resolution stub";
+ // Give it a new chance to be hot.
+ ClearMethodCounter(method, /*was_warm=*/ false);
+ return false;
}
MutexLock mu(self, *Locks::jit_lock_);
@@ -1614,7 +1610,7 @@
for (ArtMethod* m : data->GetMethods()) {
// Call the dedicated method instead of the more generic UpdateMethodsCode, because
// `m` might be in the process of being deleted.
- if (!class_linker->IsQuickResolutionStub(m->GetEntryPointFromQuickCompiledCode())) {
+ if (!m->NeedsInitializationCheck()) {
instrumentation->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
}
}