8227260: JNI upcalls should bypass class initialization barrier in c2i adapter
authorvlivanov
Fri, 19 Jul 2019 16:25:04 +0300
changeset 55749 cff8aad2593f
parent 55748 c6923eaecd7b
child 55750 6f60cfd502c3
8227260: JNI upcalls should bypass class initialization barrier in c2i adapter Reviewed-by: eosterlund, dholmes, mdoerr, dpochepk
src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
src/hotspot/cpu/s390/sharedRuntime_s390.cpp
src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
src/hotspot/share/oops/method.cpp
src/hotspot/share/oops/method.hpp
src/hotspot/share/runtime/sharedRuntime.cpp
src/hotspot/share/runtime/sharedRuntime.hpp
src/hotspot/share/runtime/thread.cpp
test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java
test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp
--- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -800,6 +800,7 @@
 #endif
 
   // Class initialization barrier for static methods
+  address c2i_no_clinit_check_entry = NULL;
   if (VM_Version::supports_fast_class_init_checks()) {
     Label L_skip_barrier;
 
@@ -812,13 +813,15 @@
     __ load_method_holder(rscratch2, rmethod);
     __ clinit_barrier(rscratch2, rscratch1, &L_skip_barrier);
     __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
+
     __ bind(L_skip_barrier);
+    c2i_no_clinit_check_entry = __ pc();
   }
 
   gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
 
   __ flush();
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
 }
 
 int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -1277,6 +1277,7 @@
   c2i_entry = __ pc();
 
   // Class initialization barrier for static methods
+  address c2i_no_clinit_check_entry = NULL;
   if (VM_Version::supports_fast_class_init_checks()) {
     Label L_skip_barrier;
 
@@ -1295,11 +1296,12 @@
     __ bctr();
 
     __ bind(L_skip_barrier);
+    c2i_no_clinit_check_entry = __ pc();
   }
 
   gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, call_interpreter, ientry);
 
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
 }
 
 #ifdef COMPILER2
--- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -2713,6 +2713,7 @@
   address c2i_entry = __ pc();
 
   // Class initialization barrier for static methods
+  address c2i_no_clinit_check_entry = NULL;
   if (VM_Version::supports_fast_class_init_checks()) {
     Label L_skip_barrier;
 
@@ -2729,11 +2730,12 @@
     __ z_br(klass);
 
     __ bind(L_skip_barrier);
+    c2i_no_clinit_check_entry = __ pc();
   }
 
   gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
 
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
 }
 
 // This function returns the adjust size (in number of words) to a c2i adapter
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -971,10 +971,8 @@
 
   address c2i_entry = __ pc();
 
-  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
-  bs->c2i_entry_barrier(masm);
-
   // Class initialization barrier for static methods
+  address c2i_no_clinit_check_entry = NULL;
   if (VM_Version::supports_fast_class_init_checks()) {
     Label L_skip_barrier;
     Register method = rbx;
@@ -993,12 +991,16 @@
     __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
 
     __ bind(L_skip_barrier);
+    c2i_no_clinit_check_entry = __ pc();
   }
 
+  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
+  bs->c2i_entry_barrier(masm);
+
   gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
 
   __ flush();
-  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+  return AdapterHandlerLibrary::new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
 }
 
 int SharedRuntime::c_calling_convention(const BasicType *sig_bt,
--- a/src/hotspot/share/oops/method.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/share/oops/method.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -146,6 +146,12 @@
   return adapter()->get_c2i_unverified_entry();
 }
 
+address Method::get_c2i_no_clinit_check_entry() {
+  assert(VM_Version::supports_fast_class_init_checks(), "");
+  assert(adapter() != NULL, "must have");
+  return adapter()->get_c2i_no_clinit_check_entry();
+}
+
 char* Method::name_and_sig_as_C_string() const {
   return name_and_sig_as_C_string(constants()->pool_holder(), name(), signature());
 }
@@ -1045,7 +1051,7 @@
                                                               _c2i_entry ---------------------------------+->[c2i entry..]
  _i2i_entry  -------------+                                   _i2c_entry ---------------+-> [i2c entry..] |
  _from_interpreted_entry  |                                   _c2i_unverified_entry     |                 |
-         |                |                                                             |                 |
+         |                |                                   _c2i_no_clinit_check_entry|                 |
          |                |  (_cds_entry_table: CODE)                                   |                 |
          |                +->[0]: jmp _entry_table[0] --> (i2i_entry_for "zero_locals") |                 |
          |                |                               (allocated at run time)       |                 |
--- a/src/hotspot/share/oops/method.hpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/share/oops/method.hpp	Fri Jul 19 16:25:04 2019 +0300
@@ -481,6 +481,7 @@
   address get_i2c_entry();
   address get_c2i_entry();
   address get_c2i_unverified_entry();
+  address get_c2i_no_clinit_check_entry();
   AdapterHandlerEntry* adapter() const {
     return constMethod()->adapter();
   }
--- a/src/hotspot/share/runtime/sharedRuntime.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -1446,7 +1446,19 @@
     guarantee(callee != NULL && callee->is_method(), "bad handshake");
     thread->set_vm_result_2(callee);
     thread->set_callee_target(NULL);
-    return callee->get_c2i_entry();
+    if (caller_frame.is_entry_frame() && VM_Version::supports_fast_class_init_checks()) {
+      // Bypass class initialization checks in c2i when caller is in native.
+      // JNI calls to static methods don't have class initialization checks.
+      // Fast class initialization checks are present in c2i adapters and call into
+      // SharedRuntime::handle_wrong_method() on the slow path.
+      //
+      // JVM upcalls may land here as well, but there's a proper check present in
+      // LinkResolver::resolve_static_call (called from JavaCalls::call_static),
+      // so bypassing it in c2i adapter is benign.
+      return callee->get_c2i_no_clinit_check_entry();
+    } else {
+      return callee->get_c2i_entry();
+    }
   }
 
   // Must be compiled to compiled path which is safe to stackwalk
@@ -2450,9 +2462,9 @@
     : BasicHashtable<mtCode>(293, (DumpSharedSpaces ? sizeof(CDSAdapterHandlerEntry) : sizeof(AdapterHandlerEntry))) { }
 
   // Create a new entry suitable for insertion in the table
-  AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+  AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry) {
     AdapterHandlerEntry* entry = (AdapterHandlerEntry*)BasicHashtable<mtCode>::new_entry(fingerprint->compute_hash());
-    entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+    entry->init(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
     if (DumpSharedSpaces) {
       ((CDSAdapterHandlerEntry*)entry)->init();
     }
@@ -2601,8 +2613,9 @@
 AdapterHandlerEntry* AdapterHandlerLibrary::new_entry(AdapterFingerPrint* fingerprint,
                                                       address i2c_entry,
                                                       address c2i_entry,
-                                                      address c2i_unverified_entry) {
-  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry);
+                                                      address c2i_unverified_entry,
+                                                      address c2i_no_clinit_check_entry) {
+  return _adapters->new_entry(fingerprint, i2c_entry, c2i_entry, c2i_unverified_entry, c2i_no_clinit_check_entry);
 }
 
 AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(const methodHandle& method) {
@@ -2778,6 +2791,7 @@
   if (base == NULL)  base = _c2i_entry;
   assert(base <= _c2i_entry || _c2i_entry == NULL, "");
   assert(base <= _c2i_unverified_entry || _c2i_unverified_entry == NULL, "");
+  assert(base <= _c2i_no_clinit_check_entry || _c2i_no_clinit_check_entry == NULL, "");
   return base;
 }
 
@@ -2791,6 +2805,8 @@
     _c2i_entry += delta;
   if (_c2i_unverified_entry != NULL)
     _c2i_unverified_entry += delta;
+  if (_c2i_no_clinit_check_entry != NULL)
+    _c2i_no_clinit_check_entry += delta;
   assert(base_address() == new_base, "");
 }
 
@@ -3129,10 +3145,20 @@
 }
 
 void AdapterHandlerEntry::print_adapter_on(outputStream* st) const {
-  st->print_cr("AHE@" INTPTR_FORMAT ": %s i2c: " INTPTR_FORMAT " c2i: " INTPTR_FORMAT " c2iUV: " INTPTR_FORMAT,
-               p2i(this), fingerprint()->as_string(),
-               p2i(get_i2c_entry()), p2i(get_c2i_entry()), p2i(get_c2i_unverified_entry()));
-
+  st->print("AHE@" INTPTR_FORMAT ": %s", p2i(this), fingerprint()->as_string());
+  if (get_i2c_entry() != NULL) {
+    st->print(" i2c: " INTPTR_FORMAT, p2i(get_i2c_entry()));
+  }
+  if (get_c2i_entry() != NULL) {
+    st->print(" c2i: " INTPTR_FORMAT, p2i(get_c2i_entry()));
+  }
+  if (get_c2i_unverified_entry() != NULL) {
+    st->print(" c2iUV: " INTPTR_FORMAT, p2i(get_c2i_unverified_entry()));
+  }
+  if (get_c2i_no_clinit_check_entry() != NULL) {
+    st->print(" c2iNCI: " INTPTR_FORMAT, p2i(get_c2i_no_clinit_check_entry()));
+  }
+  st->cr();
 }
 
 #if INCLUDE_CDS
--- a/src/hotspot/share/runtime/sharedRuntime.hpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp	Fri Jul 19 16:25:04 2019 +0300
@@ -636,6 +636,7 @@
   address _i2c_entry;
   address _c2i_entry;
   address _c2i_unverified_entry;
+  address _c2i_no_clinit_check_entry;
 
 #ifdef ASSERT
   // Captures code and signature used to generate this adapter when
@@ -644,11 +645,12 @@
   int            _saved_code_length;
 #endif
 
-  void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry) {
+  void init(AdapterFingerPrint* fingerprint, address i2c_entry, address c2i_entry, address c2i_unverified_entry, address c2i_no_clinit_check_entry) {
     _fingerprint = fingerprint;
     _i2c_entry = i2c_entry;
     _c2i_entry = c2i_entry;
     _c2i_unverified_entry = c2i_unverified_entry;
+    _c2i_no_clinit_check_entry = c2i_no_clinit_check_entry;
 #ifdef ASSERT
     _saved_code = NULL;
     _saved_code_length = 0;
@@ -661,9 +663,11 @@
   AdapterHandlerEntry();
 
  public:
-  address get_i2c_entry()            const { return _i2c_entry; }
-  address get_c2i_entry()            const { return _c2i_entry; }
-  address get_c2i_unverified_entry() const { return _c2i_unverified_entry; }
+  address get_i2c_entry()                  const { return _i2c_entry; }
+  address get_c2i_entry()                  const { return _c2i_entry; }
+  address get_c2i_unverified_entry()       const { return _c2i_unverified_entry; }
+  address get_c2i_no_clinit_check_entry()  const { return _c2i_no_clinit_check_entry; }
+
   address base_address();
   void relocate(address new_base);
 
@@ -709,7 +713,10 @@
  public:
 
   static AdapterHandlerEntry* new_entry(AdapterFingerPrint* fingerprint,
-                                        address i2c_entry, address c2i_entry, address c2i_unverified_entry);
+                                        address i2c_entry,
+                                        address c2i_entry,
+                                        address c2i_unverified_entry,
+                                        address c2i_no_clinit_check_entry = NULL);
   static void create_native_wrapper(const methodHandle& method);
   static AdapterHandlerEntry* get_adapter(const methodHandle& method);
 
--- a/src/hotspot/share/runtime/thread.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/src/hotspot/share/runtime/thread.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -2969,11 +2969,6 @@
     }
   }
 
-  // callee_target is never live across a gc point so NULL it here should
-  // it still contain a methdOop.
-
-  set_callee_target(NULL);
-
   assert(vframe_array_head() == NULL, "deopt in progress at a safepoint!");
   // If we have deferred set_locals there might be oops waiting to be
   // written
--- a/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java	Mon Jul 08 17:44:34 2019 +0200
+++ b/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java	Fri Jul 19 16:25:04 2019 +0300
@@ -27,24 +27,24 @@
  *
  * @requires !vm.graal.enabled
  *
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint                   -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint                   -DTHROW=true  ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint                   -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -Xint                   -DTHROW=true  -Xcheck:jni ClassInitBarrier
  *
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true  ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true  -Xcheck:jni ClassInitBarrier
  *
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=false ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=true  ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=false -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=true  -Xcheck:jni ClassInitBarrier
  *
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true  -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=false -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=true  -XX:CompileCommand=dontinline,*::static* ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true  -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=false -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=true  -XX:CompileCommand=dontinline,*::static* -Xcheck:jni ClassInitBarrier
  *
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true  -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=false -XX:CompileCommand=exclude,*::static* ClassInitBarrier
- * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=true  -XX:CompileCommand=exclude,*::static* ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:TieredStopAtLevel=1 -DTHROW=true  -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=false -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
+ * @run main/othervm/native -Xbatch -XX:CompileCommand=dontinline,*::test* -XX:-TieredCompilation  -DTHROW=true  -XX:CompileCommand=exclude,*::static* -Xcheck:jni ClassInitBarrier
  */
 
 import jdk.test.lib.Asserts;
@@ -70,6 +70,10 @@
     static class Test {
         static class A {
             static {
+                if (!init(B.class)) {
+                    throw new Error("init failed");
+                }
+
                 changePhase(Phase.IN_PROGRESS);
                 runTests();      // interpreted mode
                 warmup();        // trigger compilation
@@ -89,13 +93,15 @@
 
             int f;
             void m() {}
+
+            static native boolean init(Class<B> cls);
         }
 
         static class B extends A {}
 
-        static void testInvokeStatic(Runnable action)        { A.staticM(action); }
-        static void testInvokeStaticSync(Runnable action)    { A.staticS(action); }
-        static void testInvokeStaticNative(Runnable action)  { A.staticN(action); }
+        static void testInvokeStatic(Runnable action)       { A.staticM(action); }
+        static void testInvokeStaticSync(Runnable action)   { A.staticS(action); }
+        static void testInvokeStaticNative(Runnable action) { A.staticN(action); }
 
         static int  testGetStatic(Runnable action)    { int v = A.staticF; action.run(); return v;   }
         static void testPutStatic(Runnable action)    { A.staticF = 1;     action.run(); }
@@ -106,20 +112,45 @@
         static void testPutField(A recv, Runnable action)      { recv.f = 1;     action.run(); }
         static void testInvokeVirtual(A recv, Runnable action) { recv.m();       action.run(); }
 
+        static native void testInvokeStaticJNI(Runnable action);
+        static native void testInvokeStaticSyncJNI(Runnable action);
+        static native void testInvokeStaticNativeJNI(Runnable action);
+
+        static native int  testGetStaticJNI(Runnable action);
+        static native void testPutStaticJNI(Runnable action);
+        static native A    testNewInstanceAJNI(Runnable action);
+        static native B    testNewInstanceBJNI(Runnable action);
+
+        static native int  testGetFieldJNI(A recv, Runnable action);
+        static native void testPutFieldJNI(A recv, Runnable action);
+        static native void testInvokeVirtualJNI(A recv, Runnable action);
+
         static void runTests() {
             checkBlockingAction(Test::testInvokeStatic);       // invokestatic
+            checkBlockingAction(Test::testInvokeStaticSync);   // invokestatic
             checkBlockingAction(Test::testInvokeStaticNative); // invokestatic
-            checkBlockingAction(Test::testInvokeStaticSync);   // invokestatic
             checkBlockingAction(Test::testGetStatic);          // getstatic
             checkBlockingAction(Test::testPutStatic);          // putstatic
             checkBlockingAction(Test::testNewInstanceA);       // new
 
+            checkNonBlockingAction(Test::testInvokeStaticJNI);       // invokestatic
+            checkNonBlockingAction(Test::testInvokeStaticSyncJNI);   // invokestatic
+            checkNonBlockingAction(Test::testInvokeStaticNativeJNI); // invokestatic
+            checkNonBlockingAction(Test::testGetStaticJNI);          // getstatic
+            checkNonBlockingAction(Test::testPutStaticJNI);          // putstatic
+            checkBlockingAction(Test::testNewInstanceAJNI);          // new
+
             A recv = testNewInstanceB(NON_BLOCKING.get());  // trigger B initialization
             checkNonBlockingAction(Test::testNewInstanceB); // new: NO BLOCKING: same thread: A being initialized, B fully initialized
 
             checkNonBlockingAction(recv, Test::testGetField);      // getfield
             checkNonBlockingAction(recv, Test::testPutField);      // putfield
             checkNonBlockingAction(recv, Test::testInvokeVirtual); // invokevirtual
+
+            checkNonBlockingAction(Test::testNewInstanceBJNI);        // new: NO BLOCKING: same thread: A being initialized, B fully initialized
+            checkNonBlockingAction(recv, Test::testGetFieldJNI);      // getfield
+            checkNonBlockingAction(recv, Test::testPutFieldJNI);      // putfield
+            checkNonBlockingAction(recv, Test::testInvokeVirtualJNI); // invokevirtual
         }
 
         static void warmup() {
--- a/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp	Mon Jul 08 17:44:34 2019 +0200
+++ b/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp	Fri Jul 19 16:25:04 2019 +0300
@@ -25,6 +25,17 @@
 
 static jmethodID methodId;
 
+static jclass test_class_A;
+static jclass test_class_B;
+
+static jmethodID test_staticM_id;
+static jmethodID test_staticS_id;
+static jmethodID test_staticN_id;
+static jmethodID test_A_m_id;
+
+static jfieldID test_staticF_id;
+static jfieldID test_A_f_id;
+
 extern "C" {
     JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_init(JNIEnv* env, jclass cls) {
         jclass runnable = env->FindClass("java/lang/Runnable");
@@ -36,7 +47,103 @@
         return JNI_TRUE;
     }
 
+    JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_00024Test_00024A_init(JNIEnv* env, jclass cls, jclass arg1) {
+        test_class_A = (jclass)env->NewGlobalRef(cls);
+        if (test_class_A == NULL)  return JNI_FALSE;
+
+        test_class_B = (jclass)env->NewGlobalRef(arg1);
+        if (test_class_B == NULL)  return JNI_FALSE;
+
+        test_staticM_id = env->GetStaticMethodID(test_class_A, "staticM", "(Ljava/lang/Runnable;)V");
+        if (test_staticM_id == NULL)  return JNI_FALSE;
+
+        test_staticS_id = env->GetStaticMethodID(test_class_A, "staticS", "(Ljava/lang/Runnable;)V");
+        if (test_staticS_id == NULL)  return JNI_FALSE;
+
+        test_staticN_id = env->GetStaticMethodID(test_class_A, "staticN", "(Ljava/lang/Runnable;)V");
+        if (test_staticN_id == NULL)  return JNI_FALSE;
+
+        test_A_m_id = env->GetMethodID(test_class_A, "m", "()V");
+        if (test_A_m_id == NULL)  return JNI_FALSE;
+
+        test_staticF_id = env->GetStaticFieldID(test_class_A, "staticF", "I");
+        if (test_staticF_id == NULL)  return JNI_FALSE;
+
+        test_A_f_id = env->GetFieldID(test_class_A, "f", "I");
+        if (test_A_f_id == NULL)  return JNI_FALSE;
+
+        return JNI_TRUE;
+    }
+
     JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_00024A_staticN(JNIEnv* env, jclass cls, jobject action) {
         env->CallVoidMethod(action, methodId);
     }
+
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+        env->CallStaticVoidMethod(test_class_A, test_staticM_id, action);
+    }
+
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticSyncJNI(JNIEnv* env, jclass cls, jobject action) {
+        env->CallStaticVoidMethod(test_class_A, test_staticS_id, action);
+    }
+
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeStaticNativeJNI(JNIEnv* env, jclass cls, jobject action) {
+        env->CallStaticVoidMethod(test_class_A, test_staticN_id, action);
+    }
+
+    JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+        jint v = env->GetStaticIntField(test_class_A, test_staticF_id); // int v = A.staticF;
+        env->CallVoidMethod(action, methodId);                          // action.run();
+        return v;
+    }
+
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testPutStaticJNI(JNIEnv* env, jclass cls, jobject action) {
+        env->SetStaticIntField(test_class_A, test_staticF_id, 1); // A.staticF = 1;
+        env->CallVoidMethod(action, methodId);                    // action.run();
+    }
+
+    JNIEXPORT jobject JNICALL Java_ClassInitBarrier_00024Test_testNewInstanceAJNI(JNIEnv* env, jclass cls, jobject action) {
+        jobject obj = env->AllocObject(test_class_A); // A obj = new A();
+        if (env->ExceptionOccurred()) {
+          return NULL;
+        } else if (obj == NULL) {
+          jclass errorClass = env->FindClass("java/lang/AssertionError");
+          int ret = env->ThrowNew(errorClass, "JNI: AllocObject: allocation failed, but no exception thrown");
+          return NULL;
+        }
+        env->CallVoidMethod(action, methodId);        // action.run();
+        return obj;
+    }
+
+    JNIEXPORT jobject JNICALL Java_ClassInitBarrier_00024Test_testNewInstanceBJNI(JNIEnv* env, jclass cls, jobject action) {
+        jobject obj = env->AllocObject(test_class_B); // B obj = new B();
+        if (env->ExceptionOccurred()) {
+          return NULL;
+        } else if (obj == NULL) {
+          jclass errorClass = env->FindClass("java/lang/AssertionError");
+          int ret = env->ThrowNew(errorClass, "JNI: AllocObject: allocation failed, but no exception thrown");
+          return NULL;
+        }
+        env->CallVoidMethod(action, methodId);        // action.run();
+        return obj;
+    }
+
+    JNIEXPORT jint JNICALL Java_ClassInitBarrier_00024Test_testGetFieldJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+        jint v = env->GetIntField(recv, test_A_f_id); // int v = recv.f;
+        env->CallVoidMethod(action, methodId);        // action.run();
+        return v;
+    }
+
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testPutFieldJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+        env->SetIntField(recv, test_A_f_id, 1); // A.staticF = 1;
+        env->CallVoidMethod(action, methodId);  // action.run();
+    }
+
+    JNIEXPORT void JNICALL Java_ClassInitBarrier_00024Test_testInvokeVirtualJNI(JNIEnv* env, jclass cls, jobject recv, jobject action) {
+        env->CallVoidMethod(recv, test_A_m_id); // recv.m();
+        if (env->ExceptionOccurred()) {
+            return;
+        }
+        env->CallVoidMethod(action, methodId);  // action.run();
+    }
 }