JNI: Inline fast-path for `JniMethodStart()`.
Golem results for art-opt-cc (higher is better):
linux-ia32 before after
NativeDowncallStaticNormal 35.306 47.382 (+34.20%)
NativeDowncallStaticNormal6 32.951 42.247 (+28.21%)
NativeDowncallStaticNormalRefs6 17.866 41.355 (+131.5%)
NativeDowncallVirtualNormal 35.341 46.836 (+32.53%)
NativeDowncallVirtualNormal6 32.403 41.791 (+28.97%)
NativeDowncallVirtualNormalRefs6 32.131 40.500 (+26.05%)
linux-x64 before after
NativeDowncallStaticNormal 33.350 43.716 (+31.08%)
NativeDowncallStaticNormal6 31.096 43.176 (+38.85%)
NativeDowncallStaticNormalRefs6 30.617 38.500 (+25.75%)
NativeDowncallVirtualNormal 33.234 43.672 (+32.41%)
NativeDowncallVirtualNormal6 30.617 42.247 (+37.98%)
NativeDowncallVirtualNormalRefs6 32.131 42.701 (+32.90%)
linux-armv7 before after
NativeDowncallStaticNormal 7.8701 9.9651 (+26.62%)
NativeDowncallStaticNormal6 7.4147 8.9463 (+20.66%)
NativeDowncallStaticNormalRefs6 6.8830 8.3868 (+21.85%)
NativeDowncallVirtualNormal 7.8316 9.8377 (+25.61%)
NativeDowncallVirtualNormal6 7.4147 9.3596 (+26.23%)
NativeDowncallVirtualNormalRefs6 6.6794 8.4325 (+26.25%)
linux-armv8 before after
NativeDowncallStaticNormal 7.6372 9.8571 (+29.07%)
NativeDowncallStaticNormal6 7.4147 9.4905 (+28.00%)
NativeDowncallStaticNormalRefs6 6.8527 8.6705 (+26.53%)
NativeDowncallVirtualNormal 7.4147 9.3183 (+25.67%)
NativeDowncallVirtualNormal6 7.0755 9.2593 (+30.86%)
NativeDowncallVirtualNormalRefs6 6.5604 8.2967 (+26.47%)
Note that NativeDowncallStaticNormalRefs6 on x86 has been
jumping like crazy since
https://android-review.googlesource.com/1905055
between ~17.6 and ~32.4 for completely unrelated changes,
so if we take the 32.4 as a baseline, the improvement is
only ~27.6% in line with the other x86 benchmarks.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Bug: 172332525
Change-Id: I771a4765bd3a7c4e58b94be4155515241ea6fa3c
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index a222ff3..8de5c9c 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -27,6 +27,7 @@
#include "common_compiler_test.h"
#include "compiler.h"
#include "dex/dex_file.h"
+#include "entrypoints/entrypoint_utils-inl.h"
#include "gtest/gtest.h"
#include "indirect_reference_table.h"
#include "java_frame_root_info.h"
@@ -337,6 +338,8 @@
static jobject jobj_;
static jobject class_loader_;
+ static void AssertCallerObjectLocked(JNIEnv* env);
+
static LockWord GetLockWord(jobject obj);
protected:
@@ -391,53 +394,17 @@
jmethodID jmethod_;
private:
- // Helper class that overrides original entrypoints with alternative versions
- // that check that the object (`this` or class) is locked.
- class ScopedSynchronizedEntryPointOverrides {
- public:
- ScopedSynchronizedEntryPointOverrides() {
- QuickEntryPoints* qpoints = &Thread::Current()->tlsPtr_.quick_entrypoints;
- jni_method_start_original_ = qpoints->pJniMethodStart;
- qpoints->pJniMethodStart = JniMethodStartSynchronizedOverride;
- jni_method_end_original_ = qpoints->pJniMethodEnd;
- qpoints->pJniMethodEnd = JniMethodEndSynchronizedOverride;
- jni_method_end_with_reference_original_ = qpoints->pJniMethodEndWithReference;
- qpoints->pJniMethodEndWithReference = JniMethodEndWithReferenceSynchronizedOverride;
- }
-
- ~ScopedSynchronizedEntryPointOverrides() {
- QuickEntryPoints* qpoints = &Thread::Current()->tlsPtr_.quick_entrypoints;
- qpoints->pJniMethodStart = jni_method_start_original_;
- qpoints->pJniMethodEnd = jni_method_end_original_;
- qpoints->pJniMethodEndWithReference = jni_method_end_with_reference_original_;
- }
- };
-
- static void AssertCallerObjectLocked(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
- static void JniMethodStartSynchronizedOverride(Thread* self);
- static void JniMethodEndSynchronizedOverride(Thread* self);
- static mirror::Object* JniMethodEndWithReferenceSynchronizedOverride(
- jobject result, Thread* self);
-
- using JniStartType = void (*)(Thread*);
- using JniEndType = void (*)(Thread*);
- using JniEndWithReferenceType = mirror::Object* (*)(jobject, Thread*);
-
- static JniStartType jni_method_start_original_;
- static JniEndType jni_method_end_original_;
- static JniEndWithReferenceType jni_method_end_with_reference_original_;
-
bool check_generic_jni_;
};
jclass JniCompilerTest::jklass_;
jobject JniCompilerTest::jobj_;
jobject JniCompilerTest::class_loader_;
-JniCompilerTest::JniStartType JniCompilerTest::jni_method_start_original_;
-JniCompilerTest::JniEndType JniCompilerTest::jni_method_end_original_;
-JniCompilerTest::JniEndWithReferenceType JniCompilerTest::jni_method_end_with_reference_original_;
-void JniCompilerTest::AssertCallerObjectLocked(Thread* self) {
+void JniCompilerTest::AssertCallerObjectLocked(JNIEnv* env) {
+ Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+ CHECK_EQ(self, Thread::Current());
+ ScopedObjectAccess soa(self);
ArtMethod** caller_frame = self->GetManagedStack()->GetTopQuickFrame();
CHECK(caller_frame != nullptr);
ArtMethod* caller = *caller_frame;
@@ -447,7 +414,10 @@
CHECK(!caller->IsCriticalNative());
CHECK(caller->IsSynchronized());
ObjPtr<mirror::Object> lock;
- if (caller->IsStatic()) {
+ if (self->GetManagedStack()->GetTopQuickFrameTag()) {
+ // Generic JNI.
+ lock = GetGenericJniSynchronizationObject(self, caller);
+ } else if (caller->IsStatic()) {
lock = caller->GetDeclaringClass();
} else {
uint8_t* sp = reinterpret_cast<uint8_t*>(caller_frame);
@@ -461,23 +431,6 @@
CHECK_EQ(Monitor::GetLockOwnerThreadId(lock), self->GetThreadId());
}
-void JniCompilerTest::JniMethodStartSynchronizedOverride(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
- AssertCallerObjectLocked(self);
- jni_method_start_original_(self);
-}
-
-void JniCompilerTest::JniMethodEndSynchronizedOverride(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
- jni_method_end_original_(self);
- AssertCallerObjectLocked(self);
-}
-
-mirror::Object* JniCompilerTest::JniMethodEndWithReferenceSynchronizedOverride(
- jobject result, Thread* self) NO_THREAD_SAFETY_ANALYSIS {
- mirror::Object* raw_result = jni_method_end_with_reference_original_(result, self);
- AssertCallerObjectLocked(self);
- return raw_result;
-}
-
LockWord JniCompilerTest::GetLockWord(jobject obj) {
ScopedObjectAccess soa(Thread::Current());
return soa.Decode<mirror::Object>(obj)->GetLockWord(/*as_volatile=*/ false);
@@ -886,7 +839,8 @@
}
int gJava_MyClassNatives_fooJJ_synchronized_calls[kJniKindCount] = {};
-jlong Java_MyClassNatives_fooJJ_synchronized(JNIEnv*, jobject, jlong x, jlong y) {
+jlong Java_MyClassNatives_fooJJ_synchronized(JNIEnv* env, jobject, jlong x, jlong y) {
+ JniCompilerTest::AssertCallerObjectLocked(env);
gJava_MyClassNatives_fooJJ_synchronized_calls[gCurrentJni]++;
return x | y;
}
@@ -894,7 +848,6 @@
void JniCompilerTest::CompileAndRun_fooJJ_synchronizedImpl() {
SetUpForTest(false, "fooJJ_synchronized", "(JJ)J",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooJJ_synchronized));
- ScopedSynchronizedEntryPointOverrides ssepo;
EXPECT_EQ(0, gJava_MyClassNatives_fooJJ_synchronized_calls[gCurrentJni]);
jlong a = 0x1000000020000000ULL;
@@ -1220,7 +1173,8 @@
JNI_TEST(CompileAndRunStaticIntObjectObjectMethod)
int gJava_MyClassNatives_fooSSIOO_calls[kJniKindCount] = {};
-jobject Java_MyClassNatives_fooSSIOO(JNIEnv*, jclass klass, jint x, jobject y, jobject z) {
+jobject Java_MyClassNatives_fooSSIOO(JNIEnv* env, jclass klass, jint x, jobject y, jobject z) {
+ JniCompilerTest::AssertCallerObjectLocked(env);
gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]++;
switch (x) {
case 1:
@@ -1236,7 +1190,6 @@
SetUpForTest(true, "fooSSIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_fooSSIOO));
- ScopedSynchronizedEntryPointOverrides ssepo;
EXPECT_EQ(0, gJava_MyClassNatives_fooSSIOO_calls[gCurrentJni]);
jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, nullptr, nullptr);
@@ -1505,7 +1458,8 @@
JNI_TEST(GetText)
int gJava_MyClassNatives_GetSinkProperties_calls[kJniKindCount] = {};
-jarray Java_MyClassNatives_GetSinkProperties(JNIEnv*, jobject thisObj, jstring s) {
+jarray Java_MyClassNatives_GetSinkProperties(JNIEnv* env, jobject thisObj, jstring s) {
+ JniCompilerTest::AssertCallerObjectLocked(env);
EXPECT_EQ(s, nullptr);
gJava_MyClassNatives_GetSinkProperties_calls[gCurrentJni]++;
@@ -1518,7 +1472,6 @@
void JniCompilerTest::GetSinkPropertiesNativeImpl() {
SetUpForTest(false, "getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
CURRENT_JNI_WRAPPER(Java_MyClassNatives_GetSinkProperties));
- ScopedSynchronizedEntryPointOverrides ssepo;
EXPECT_EQ(0, gJava_MyClassNatives_GetSinkProperties_calls[gCurrentJni]);
jarray result = down_cast<jarray>(