8223213: Implement fast class initialization checks on x86-64
authorvlivanov
Thu, 30 May 2019 13:39:13 +0300
changeset 55105 9ad765641e8f
parent 55104 a72e187adb54
child 55106 ebc4e5a625e2
8223213: Implement fast class initialization checks on x86-64 Reviewed-by: kvn, redestad, dholmes, mdoerr, coleenp
src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp
src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp
src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp
src/hotspot/cpu/x86/interp_masm_x86.cpp
src/hotspot/cpu/x86/interp_masm_x86.hpp
src/hotspot/cpu/x86/macroAssembler_x86.cpp
src/hotspot/cpu/x86/macroAssembler_x86.hpp
src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
src/hotspot/cpu/x86/templateTable_x86.cpp
src/hotspot/cpu/x86/vm_version_x86.hpp
src/hotspot/cpu/x86/x86_64.ad
src/hotspot/share/c1/c1_LIRAssembler.cpp
src/hotspot/share/c1/c1_LIRAssembler.hpp
src/hotspot/share/ci/ciMethod.cpp
src/hotspot/share/ci/ciMethod.hpp
src/hotspot/share/oops/cpCache.cpp
src/hotspot/share/oops/method.cpp
src/hotspot/share/oops/method.hpp
src/hotspot/share/oops/methodData.hpp
src/hotspot/share/opto/compile.cpp
src/hotspot/share/opto/compile.hpp
src/hotspot/share/opto/parse.hpp
src/hotspot/share/opto/parse1.cpp
src/hotspot/share/runtime/deoptimization.cpp
src/hotspot/share/runtime/deoptimization.hpp
src/hotspot/share/runtime/sharedRuntime.cpp
src/hotspot/share/runtime/vmStructs.cpp
src/hotspot/share/runtime/vm_version.hpp
test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java
test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp	Thu May 30 13:39:13 2019 +0300
@@ -316,6 +316,9 @@
   return start_offset;
 }
 
+void LIR_Assembler::clinit_barrier(ciMethod* method) {
+  ShouldNotReachHere(); // not implemented
+}
 
 void LIR_Assembler::jobject2reg(jobject o, Register reg) {
   if (o == NULL) {
--- a/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp	Thu May 30 13:39:13 2019 +0300
@@ -191,6 +191,9 @@
   return offset;
 }
 
+void LIR_Assembler::clinit_barrier(ciMethod* method) {
+  ShouldNotReachHere(); // not implemented
+}
 
 void LIR_Assembler::jobject2reg_with_patching(Register reg, CodeEmitInfo* info) {
   jobject o = (jobject)Universe::non_oop_word();
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp	Thu May 30 13:39:13 2019 +0300
@@ -79,6 +79,9 @@
   return offset;
 }
 
+void LIR_Assembler::clinit_barrier(ciMethod* method) {
+  ShouldNotReachHere(); // not implemented
+}
 
 void LIR_Assembler::osr_entry() {
   // On-stack-replacement entry sequence:
--- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp	Thu May 30 13:39:13 2019 +0300
@@ -81,6 +81,10 @@
   return offset;
 }
 
+void LIR_Assembler::clinit_barrier(ciMethod* method) {
+  ShouldNotReachHere(); // not implemented
+}
+
 void LIR_Assembler::osr_entry() {
   // On-stack-replacement entry sequence (interpreter frame layout described in interpreter_sparc.cpp):
   //
--- a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp	Thu May 30 13:39:13 2019 +0300
@@ -172,6 +172,9 @@
   return offset;
 }
 
+void LIR_Assembler::clinit_barrier(ciMethod* method) {
+  ShouldNotReachHere(); // not implemented
+}
 
 void LIR_Assembler::osr_entry() {
   // On-stack-replacement entry sequence (interpreter frame layout described in interpreter_sparc.cpp):
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp	Thu May 30 13:39:13 2019 +0300
@@ -359,6 +359,23 @@
   return offset;
 }
 
+void LIR_Assembler::clinit_barrier(ciMethod* method) {
+  assert(VM_Version::supports_fast_class_init_checks(), "sanity");
+  assert(method->holder()->is_being_initialized() || method->holder()->is_initialized(),
+         "initialization should have been started");
+
+  Label L_skip_barrier;
+  Register klass = rscratch1;
+  Register thread = LP64_ONLY( r15_thread ) NOT_LP64( noreg );
+  assert(thread != noreg, "x86_32 not implemented");
+
+  __ mov_metadata(klass, method->holder()->constant_encoding());
+  __ clinit_barrier(klass, thread, &L_skip_barrier /*L_fast_path*/);
+
+  __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub()));
+
+  __ bind(L_skip_barrier);
+}
 
 void LIR_Assembler::jobject2reg_with_patching(Register reg, CodeEmitInfo* info) {
   jobject o = NULL;
--- a/src/hotspot/cpu/x86/interp_masm_x86.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp	Thu May 30 13:39:13 2019 +0300
@@ -487,7 +487,8 @@
                                                                Register tmp,
                                                                int bcp_offset,
                                                                size_t index_size) {
-  assert(cache != tmp, "must use different register");
+  assert_different_registers(cache, tmp);
+
   get_cache_index_at_bcp(tmp, bcp_offset, index_size);
   assert(sizeof(ConstantPoolCacheEntry) == 4 * wordSize, "adjust code below");
   // convert from field index to ConstantPoolCacheEntry index
@@ -501,8 +502,9 @@
 }
 
 // Load object from cpool->resolved_references(index)
-void InterpreterMacroAssembler::load_resolved_reference_at_index(
-                                           Register result, Register index, Register tmp) {
+void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result,
+                                                                 Register index,
+                                                                 Register tmp) {
   assert_different_registers(result, index);
 
   get_constant_pool(result);
@@ -516,14 +518,32 @@
 }
 
 // load cpool->resolved_klass_at(index)
-void InterpreterMacroAssembler::load_resolved_klass_at_index(Register cpool,
-                                           Register index, Register klass) {
+void InterpreterMacroAssembler::load_resolved_klass_at_index(Register klass,
+                                                             Register cpool,
+                                                             Register index) {
+  assert_different_registers(cpool, index);
+
   movw(index, Address(cpool, index, Address::times_ptr, sizeof(ConstantPool)));
   Register resolved_klasses = cpool;
   movptr(resolved_klasses, Address(cpool, ConstantPool::resolved_klasses_offset_in_bytes()));
   movptr(klass, Address(resolved_klasses, index, Address::times_ptr, Array<Klass*>::base_offset_in_bytes()));
 }
 
+void InterpreterMacroAssembler::load_resolved_method_at_index(int byte_no,
+                                                              Register method,
+                                                              Register cache,
+                                                              Register index) {
+  assert_different_registers(cache, index);
+
+  const int method_offset = in_bytes(
+    ConstantPoolCache::base_offset() +
+      ((byte_no == TemplateTable::f2_byte)
+       ? ConstantPoolCacheEntry::f2_offset()
+       : ConstantPoolCacheEntry::f1_offset()));
+
+  movptr(method, Address(cache, index, Address::times_ptr, method_offset)); // get f1 Method*
+}
+
 // Generate a subtype check: branch to ok_is_subtype if sub_klass is a
 // subtype of super_klass.
 //
--- a/src/hotspot/cpu/x86/interp_masm_x86.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/interp_masm_x86.hpp	Thu May 30 13:39:13 2019 +0300
@@ -124,9 +124,14 @@
   void load_resolved_reference_at_index(Register result, Register index, Register tmp = rscratch2);
 
   // load cpool->resolved_klass_at(index)
-  void load_resolved_klass_at_index(Register cpool,  // the constant pool (corrupted on return)
-                                    Register index,  // the constant pool index (corrupted on return)
-                                    Register klass); // contains the Klass on return
+  void load_resolved_klass_at_index(Register klass,  // contains the Klass on return
+                                    Register cpool,  // the constant pool (corrupted on return)
+                                    Register index); // the constant pool index (corrupted on return)
+
+  void load_resolved_method_at_index(int byte_no,
+                                     Register method,
+                                     Register cache,
+                                     Register index);
 
   NOT_LP64(void f2ieee();)        // truncate ftos to 32bits
   NOT_LP64(void d2ieee();)        // truncate dtos to 64bits
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Thu May 30 13:39:13 2019 +0300
@@ -4603,6 +4603,29 @@
 }
 
 
+void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fast_path, Label* L_slow_path) {
+  assert(L_fast_path != NULL || L_slow_path != NULL, "at least one is required");
+
+  Label L_fallthrough;
+  if (L_fast_path == NULL) {
+    L_fast_path = &L_fallthrough;
+  }
+
+  // Fast path check: class is fully initialized
+  cmpb(Address(klass, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized);
+  jcc(Assembler::equal, *L_fast_path);
+
+  // Fast path check: current thread is initializer thread
+  cmpptr(thread, Address(klass, InstanceKlass::init_thread_offset()));
+  if (L_slow_path != NULL) {
+    jcc(Assembler::notEqual, *L_slow_path);
+  } else {
+    jcc(Assembler::equal, *L_fast_path);
+  }
+
+  bind(L_fallthrough);
+}
+
 void MacroAssembler::cmov32(Condition cc, Register dst, Address src) {
   if (VM_Version::supports_cmov()) {
     cmovl(cc, dst, src);
@@ -5195,20 +5218,22 @@
 void MacroAssembler::load_mirror(Register mirror, Register method, Register tmp) {
   // get mirror
   const int mirror_offset = in_bytes(Klass::java_mirror_offset());
-  movptr(mirror, Address(method, Method::const_offset()));
-  movptr(mirror, Address(mirror, ConstMethod::constants_offset()));
-  movptr(mirror, Address(mirror, ConstantPool::pool_holder_offset_in_bytes()));
+  load_method_holder(mirror, method);
   movptr(mirror, Address(mirror, mirror_offset));
   resolve_oop_handle(mirror, tmp);
 }
 
 void MacroAssembler::load_method_holder_cld(Register rresult, Register rmethod) {
-  movptr(rresult, Address(rmethod, Method::const_offset()));
-  movptr(rresult, Address(rresult, ConstMethod::constants_offset()));
-  movptr(rresult, Address(rresult, ConstantPool::pool_holder_offset_in_bytes()));
+  load_method_holder(rresult, rmethod);
   movptr(rresult, Address(rresult, InstanceKlass::class_loader_data_offset()));
 }
 
+void MacroAssembler::load_method_holder(Register holder, Register method) {
+  movptr(holder, Address(method, Method::const_offset()));                      // ConstMethod*
+  movptr(holder, Address(holder, ConstMethod::constants_offset()));             // ConstantPool*
+  movptr(holder, Address(holder, ConstantPool::pool_holder_offset_in_bytes())); // InstanceKlass*
+}
+
 void MacroAssembler::load_klass(Register dst, Register src) {
 #ifdef _LP64
   if (UseCompressedClassPointers) {
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp	Thu May 30 13:39:13 2019 +0300
@@ -317,6 +317,8 @@
   void load_mirror(Register mirror, Register method, Register tmp = rscratch2);
   void load_method_holder_cld(Register rresult, Register rmethod);
 
+  void load_method_holder(Register holder, Register method);
+
   // oop manipulations
   void load_klass(Register dst, Register src);
   void store_klass(Register dst, Register src);
@@ -581,6 +583,11 @@
                            Register temp_reg,
                            Label& L_success);
 
+  void clinit_barrier(Register klass,
+                      Register thread,
+                      Label* L_fast_path = NULL,
+                      Label* L_slow_path = NULL);
+
   // method handles (JSR 292)
   Address argument_address(RegisterOrConstant arg_slot, int extra_slot_offset = 0);
 
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp	Thu May 30 13:39:13 2019 +0300
@@ -974,6 +974,27 @@
   BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
   bs->c2i_entry_barrier(masm);
 
+  // Class initialization barrier for static methods
+  if (VM_Version::supports_fast_class_init_checks()) {
+    Label L_skip_barrier;
+    Register method = rbx;
+
+    { // Bypass the barrier for non-static methods
+      Register flags  = rscratch1;
+      __ movl(flags, Address(method, Method::access_flags_offset()));
+      __ testl(flags, JVM_ACC_STATIC);
+      __ jcc(Assembler::zero, L_skip_barrier); // non-static
+    }
+
+    Register klass = rscratch1;
+    __ load_method_holder(klass, method);
+    __ clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+
+    __ bind(L_skip_barrier);
+  }
+
   gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
 
   __ flush();
@@ -2140,6 +2161,17 @@
 
   int vep_offset = ((intptr_t)__ pc()) - start;
 
+  if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
+    Label L_skip_barrier;
+    Register klass = r10;
+    __ mov_metadata(klass, method->method_holder()); // InstanceKlass*
+    __ clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+
+    __ bind(L_skip_barrier);
+  }
+
 #ifdef COMPILER1
   // For Object.hashCode, System.identityHashCode try to pull hashCode from object header if available.
   if ((InlineObjectHash && method->intrinsic_id() == vmIntrinsics::_hashCode) || (method->intrinsic_id() == vmIntrinsics::_identityHashCode)) {
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp	Thu May 30 13:39:13 2019 +0300
@@ -2719,12 +2719,13 @@
 }
 
 void TemplateTable::resolve_cache_and_index(int byte_no,
-                                            Register Rcache,
+                                            Register cache,
                                             Register index,
                                             size_t index_size) {
   const Register temp = rbx;
-  assert_different_registers(Rcache, index, temp);
-
+  assert_different_registers(cache, index, temp);
+
+  Label L_clinit_barrier_slow;
   Label resolved;
 
   Bytecodes::Code code = bytecode();
@@ -2735,17 +2736,32 @@
   }
 
   assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range");
-  __ get_cache_and_index_and_bytecode_at_bcp(Rcache, index, temp, byte_no, 1, index_size);
+  __ get_cache_and_index_and_bytecode_at_bcp(cache, index, temp, byte_no, 1, index_size);
   __ cmpl(temp, code);  // have we resolved this bytecode?
   __ jcc(Assembler::equal, resolved);
 
   // resolve first time through
+  // Class initialization barrier slow path lands here as well.
+  __ bind(L_clinit_barrier_slow);
   address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
   __ movl(temp, code);
   __ call_VM(noreg, entry, temp);
   // Update registers with resolved info
-  __ get_cache_and_index_at_bcp(Rcache, index, 1, index_size);
+  __ get_cache_and_index_at_bcp(cache, index, 1, index_size);
+
   __ bind(resolved);
+
+  // Class initialization barrier for static methods
+  if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) {
+    const Register method = temp;
+    const Register klass  = temp;
+    const Register thread = LP64_ONLY(r15_thread) NOT_LP64(noreg);
+    assert(thread != noreg, "x86_32 not supported");
+
+    __ load_resolved_method_at_index(byte_no, method, cache, index);
+    __ load_method_holder(klass, method);
+    __ clinit_barrier(klass, thread, NULL /*L_fast_path*/, &L_clinit_barrier_slow);
+  }
 }
 
 // The cache and index registers must be set before call
@@ -2794,11 +2810,6 @@
   assert_different_registers(itable_index, cache, index);
   // determine constant pool cache field offsets
   assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant");
-  const int method_offset = in_bytes(
-    ConstantPoolCache::base_offset() +
-      ((byte_no == f2_byte)
-       ? ConstantPoolCacheEntry::f2_offset()
-       : ConstantPoolCacheEntry::f1_offset()));
   const int flags_offset = in_bytes(ConstantPoolCache::base_offset() +
                                     ConstantPoolCacheEntry::flags_offset());
   // access constant pool cache fields
@@ -2807,7 +2818,7 @@
 
   size_t index_size = (is_invokedynamic ? sizeof(u4) : sizeof(u2));
   resolve_cache_and_index(byte_no, cache, index, index_size);
-    __ movptr(method, Address(cache, index, Address::times_ptr, method_offset));
+  __ load_resolved_method_at_index(byte_no, method, cache, index);
 
   if (itable_index != noreg) {
     // pick up itable or appendix index from f2 also:
@@ -3862,9 +3873,7 @@
   __ profile_virtual_call(rdx, rbcp, rlocals);
 
   // Get declaring interface class from method, and itable index
-  __ movptr(rax, Address(rbx, Method::const_offset()));
-  __ movptr(rax, Address(rax, ConstMethod::constants_offset()));
-  __ movptr(rax, Address(rax, ConstantPool::pool_holder_offset_in_bytes()));
+  __ load_method_holder(rax, rbx);
   __ movl(rbx, Address(rbx, Method::itable_index_offset()));
   __ subl(rbx, Method::itable_index_max);
   __ negl(rbx);
@@ -4003,7 +4012,7 @@
   __ jcc(Assembler::notEqual, slow_case_no_pop);
 
   // get InstanceKlass
-  __ load_resolved_klass_at_index(rcx, rdx, rcx);
+  __ load_resolved_klass_at_index(rcx, rcx, rdx);
   __ push(rcx);  // save the contexts of klass for initializing the header
 
   // make sure klass is initialized & doesn't have finalizer
@@ -4197,7 +4206,7 @@
   // Get superklass in rax and subklass in rbx
   __ bind(quicked);
   __ mov(rdx, rax); // Save object in rdx; rax needed for subtype check
-  __ load_resolved_klass_at_index(rcx, rbx, rax);
+  __ load_resolved_klass_at_index(rax, rcx, rbx);
 
   __ bind(resolved);
   __ load_klass(rbx, rdx);
@@ -4263,7 +4272,7 @@
   // Get superklass in rax and subklass in rdx
   __ bind(quicked);
   __ load_klass(rdx, rax);
-  __ load_resolved_klass_at_index(rcx, rbx, rax);
+  __ load_resolved_klass_at_index(rax, rcx, rbx);
 
   __ bind(resolved);
 
--- a/src/hotspot/cpu/x86/vm_version_x86.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/vm_version_x86.hpp	Thu May 30 13:39:13 2019 +0300
@@ -936,6 +936,11 @@
   // the intrinsic for java.lang.Thread.onSpinWait()
   static bool supports_on_spin_wait() { return supports_sse2(); }
 
+  // x86_64 supports fast class initialization checks for static methods.
+  static bool supports_fast_class_init_checks() {
+    return LP64_ONLY(true) NOT_LP64(false); // not implemented on x86_32
+  }
+
   // support functions for virtualization detection
  private:
   static void check_virt_cpuid(uint32_t idx, uint32_t *regs);
--- a/src/hotspot/cpu/x86/x86_64.ad	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/cpu/x86/x86_64.ad	Thu May 30 13:39:13 2019 +0300
@@ -874,6 +874,22 @@
   int framesize = C->frame_size_in_bytes();
   int bangsize = C->bang_size_in_bytes();
 
+  if (C->clinit_barrier_on_entry()) {
+    assert(VM_Version::supports_fast_class_init_checks(), "sanity");
+    assert(C->method()->holder()->is_being_initialized() || C->method()->holder()->is_initialized(),
+           "initialization should have been started");
+
+    Label L_skip_barrier;
+    Register klass = rscratch1;
+
+    __ mov_metadata(klass, C->method()->holder()->constant_encoding());
+    __ clinit_barrier(klass, r15_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); // slow path
+
+    __ bind(L_skip_barrier);
+  }
+
   __ verified_entry(framesize, C->need_stack_bang(bangsize)?bangsize:0, false, C->stub_function() != NULL);
 
   C->set_frame_complete(cbuf.insts_size());
--- a/src/hotspot/share/c1/c1_LIRAssembler.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/c1/c1_LIRAssembler.cpp	Thu May 30 13:39:13 2019 +0300
@@ -162,6 +162,9 @@
   return !method->is_static();
 }
 
+bool LIR_Assembler::needs_clinit_barrier_on_entry(ciMethod* method) const {
+  return VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier();
+}
 
 int LIR_Assembler::code_offset() const {
   return _masm->offset();
@@ -621,6 +624,9 @@
       }
       offsets()->set_value(CodeOffsets::Verified_Entry, _masm->offset());
       _masm->verified_entry();
+      if (needs_clinit_barrier_on_entry(compilation()->method())) {
+        clinit_barrier(compilation()->method());
+      }
       build_frame();
       offsets()->set_value(CodeOffsets::Frame_Complete, _masm->offset());
       break;
--- a/src/hotspot/share/c1/c1_LIRAssembler.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/c1/c1_LIRAssembler.hpp	Thu May 30 13:39:13 2019 +0300
@@ -81,6 +81,9 @@
   // returns offset of icache check
   int check_icache();
 
+  bool needs_clinit_barrier_on_entry(ciMethod* method) const;
+  void clinit_barrier(ciMethod* method);
+
   void jobject2reg(jobject o, Register reg);
   void jobject2reg_with_patching(Register reg, CodeEmitInfo* info);
 
--- a/src/hotspot/share/ci/ciMethod.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/ci/ciMethod.cpp	Thu May 30 13:39:13 2019 +0300
@@ -933,6 +933,13 @@
   return get_Method()->is_ignored_by_security_stack_walk();
 }
 
+// ------------------------------------------------------------------
+// ciMethod::needs_clinit_barrier
+//
+bool ciMethod::needs_clinit_barrier() const {
+  check_is_loaded();
+  return is_static() && !holder()->is_initialized();
+}
 
 // ------------------------------------------------------------------
 // invokedynamic support
--- a/src/hotspot/share/ci/ciMethod.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/ci/ciMethod.hpp	Thu May 30 13:39:13 2019 +0300
@@ -245,6 +245,8 @@
 
   ResourceBitMap live_local_oops_at_bci(int bci);
 
+  bool needs_clinit_barrier() const;
+
 #ifdef COMPILER1
   const BitMap& bci_block_start();
 #endif
--- a/src/hotspot/share/oops/cpCache.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/oops/cpCache.cpp	Thu May 30 13:39:13 2019 +0300
@@ -261,11 +261,22 @@
         method->name() != vmSymbols::object_initializer_name()) {
       do_resolve = false;
     }
-    // Don't mark invokestatic to method as resolved if the holder class has not yet completed
-    // initialization. An invokestatic must only proceed if the class is initialized, but if
-    // we resolve it before then that class initialization check is skipped.
-    if (invoke_code == Bytecodes::_invokestatic && !method->method_holder()->is_initialized()) {
-      do_resolve = false;
+    if (invoke_code == Bytecodes::_invokestatic) {
+      assert(method->method_holder()->is_initialized() ||
+             method->method_holder()->is_reentrant_initialization(Thread::current()),
+             "invalid class initialization state for invoke_static");
+
+      if (!VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
+        // Don't mark invokestatic to method as resolved if the holder class has not yet completed
+        // initialization. An invokestatic must only proceed if the class is initialized, but if
+        // we resolve it before then that class initialization check is skipped.
+        //
+        // When fast class initialization checks are supported (VM_Version::supports_fast_class_init_checks() == true),
+        // template interpreter supports fast class initialization check for
+        // invokestatic which doesn't require call site re-resolution to
+        // enforce class initialization barrier.
+        do_resolve = false;
+      }
     }
     if (do_resolve) {
       set_bytecode_1(invoke_code);
--- a/src/hotspot/share/oops/method.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/oops/method.cpp	Thu May 30 13:39:13 2019 +0300
@@ -704,6 +704,10 @@
    return name() == vmSymbols::object_initializer_name();
 }
 
+bool Method::needs_clinit_barrier() const {
+  return is_static() && !method_holder()->is_initialized();
+}
+
 objArrayHandle Method::resolved_checked_exceptions_impl(Method* method, TRAPS) {
   int length = method->checked_exceptions_length();
   if (length == 0) {  // common case
--- a/src/hotspot/share/oops/method.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/oops/method.hpp	Thu May 30 13:39:13 2019 +0300
@@ -699,6 +699,8 @@
   bool has_aot_code() const                      { return aot_code() != NULL; }
 #endif
 
+  bool needs_clinit_barrier() const;
+
   // sizing
   static int header_size()                       {
     return align_up((int)sizeof(Method), wordSize) / wordSize;
--- a/src/hotspot/share/oops/methodData.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/oops/methodData.hpp	Thu May 30 13:39:13 2019 +0300
@@ -2012,7 +2012,7 @@
 
   // Whole-method sticky bits and flags
   enum {
-    _trap_hist_limit    = 24 JVMCI_ONLY(+5),   // decoupled from Deoptimization::Reason_LIMIT
+    _trap_hist_limit    = 25 JVMCI_ONLY(+5),   // decoupled from Deoptimization::Reason_LIMIT
     _trap_hist_mask     = max_jubyte,
     _extra_data_count   = 4     // extra DataLayout headers, for trap history
   }; // Public flag values
--- a/src/hotspot/share/opto/compile.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/opto/compile.cpp	Thu May 30 13:39:13 2019 +0300
@@ -654,6 +654,7 @@
                   _trace_opto_output(directive->TraceOptoOutputOption),
 #endif
                   _has_method_handle_invokes(false),
+                  _clinit_barrier_on_entry(false),
                   _comp_arena(mtCompiler),
                   _barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
                   _env(ci_env),
@@ -988,6 +989,7 @@
     _trace_opto_output(directive->TraceOptoOutputOption),
 #endif
     _has_method_handle_invokes(false),
+    _clinit_barrier_on_entry(false),
     _comp_arena(mtCompiler),
     _env(ci_env),
     _directive(directive),
@@ -1170,6 +1172,9 @@
     }
   }
 #endif
+  if (VM_Version::supports_fast_class_init_checks() && has_method() && !is_osr_compilation() && method()->needs_clinit_barrier()) {
+    set_clinit_barrier_on_entry(true);
+  }
   if (debug_info()->recording_non_safepoints()) {
     set_node_note_array(new(comp_arena()) GrowableArray<Node_Notes*>
                         (comp_arena(), 8, 0, NULL));
--- a/src/hotspot/share/opto/compile.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/opto/compile.hpp	Thu May 30 13:39:13 2019 +0300
@@ -416,6 +416,7 @@
   bool                  _has_method_handle_invokes; // True if this method has MethodHandle invokes.
   RTMState              _rtm_state;             // State of Restricted Transactional Memory usage
   int                   _loop_opts_cnt;         // loop opts round
+  bool                  _clinit_barrier_on_entry; // True if clinit barrier is needed on nmethod entry
 
   // Compilation environment.
   Arena                 _comp_arena;            // Arena with lifetime equivalent to Compile
@@ -714,6 +715,8 @@
   bool          profile_rtm() const              { return _rtm_state == ProfileRTM; }
   uint              max_node_limit() const       { return (uint)_max_node_limit; }
   void          set_max_node_limit(uint n)       { _max_node_limit = n; }
+  bool              clinit_barrier_on_entry()       { return _clinit_barrier_on_entry; }
+  void          set_clinit_barrier_on_entry(bool z) { _clinit_barrier_on_entry = z; }
 
   // check the CompilerOracle for special behaviours for this compile
   bool          method_has_option(const char * option) {
--- a/src/hotspot/share/opto/parse.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/opto/parse.hpp	Thu May 30 13:39:13 2019 +0300
@@ -482,6 +482,8 @@
   // Helper function to compute array addressing
   Node* array_addressing(BasicType type, int vals, const Type* *result2=NULL);
 
+  void clinit_deopt();
+
   void rtm_deopt();
 
   // Pass current map to exits
--- a/src/hotspot/share/opto/parse1.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/opto/parse1.cpp	Thu May 30 13:39:13 2019 +0300
@@ -584,6 +584,11 @@
   }
 
   if (depth() == 1 && !failing()) {
+    if (C->clinit_barrier_on_entry()) {
+      // Add check to deoptimize the nmethod once the holder class is fully initialized
+      clinit_deopt();
+    }
+
     // Add check to deoptimize the nmethod if RTM state was changed
     rtm_deopt();
   }
@@ -1192,7 +1197,7 @@
 // The main thing to do is lock the receiver of a synchronized method.
 void Parse::do_method_entry() {
   set_parse_bci(InvocationEntryBci); // Pseudo-BCP
-  set_sp(0);                      // Java Stack Pointer
+  set_sp(0);                         // Java Stack Pointer
 
   NOT_PRODUCT( count_compiled_calls(true/*at_method_entry*/, false/*is_inline*/); )
 
@@ -2102,11 +2107,36 @@
   set_control( _gvn.transform(result_rgn) );
 }
 
+// Add check to deoptimize once holder klass is fully initialized.
+void Parse::clinit_deopt() {
+  assert(C->has_method(), "only for normal compilations");
+  assert(depth() == 1, "only for main compiled method");
+  assert(is_normal_parse(), "no barrier needed on osr entry");
+  assert(method()->holder()->is_being_initialized() || method()->holder()->is_initialized(),
+         "initialization should have been started");
+
+  set_parse_bci(0);
+
+  Node* holder = makecon(TypeKlassPtr::make(method()->holder()));
+  int init_state_off = in_bytes(InstanceKlass::init_state_offset());
+  Node* adr = basic_plus_adr(top(), holder, init_state_off);
+  Node* init_state = make_load(control(), adr, TypeInt::BYTE, T_BYTE, MemNode::unordered);
+
+  Node* fully_initialized_state = makecon(TypeInt::make(InstanceKlass::fully_initialized));
+
+  Node* chk = gvn().transform(new CmpINode(init_state, fully_initialized_state));
+  Node* tst = gvn().transform(new BoolNode(chk, BoolTest::ne));
+
+  { BuildCutout unless(this, tst, PROB_MAX);
+    uncommon_trap(Deoptimization::Reason_initialized, Deoptimization::Action_reinterpret);
+  }
+}
+
 // Add check to deoptimize if RTM state is not ProfileRTM
 void Parse::rtm_deopt() {
 #if INCLUDE_RTM_OPT
   if (C->profile_rtm()) {
-    assert(C->method() != NULL, "only for normal compilations");
+    assert(C->has_method(), "only for normal compilations");
     assert(!C->method()->method_data()->is_empty(), "MDO is needed to record RTM state");
     assert(depth() == 1, "generate check only for main compiled method");
 
--- a/src/hotspot/share/runtime/deoptimization.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/runtime/deoptimization.cpp	Thu May 30 13:39:13 2019 +0300
@@ -2176,6 +2176,7 @@
   "profile_predicate",
   "unloaded",
   "uninitialized",
+  "initialized",
   "unreached",
   "unhandled",
   "constraint",
--- a/src/hotspot/share/runtime/deoptimization.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/runtime/deoptimization.hpp	Thu May 30 13:39:13 2019 +0300
@@ -72,6 +72,7 @@
     // recorded per method
     Reason_unloaded,              // unloaded class or constant pool entry
     Reason_uninitialized,         // bad class state (uninitialized)
+    Reason_initialized,           // class has been fully initialized
     Reason_unreached,             // code is not reached, compiler
     Reason_unhandled,             // arbitrary compiler limitation
     Reason_constraint,            // arbitrary runtime constraint violated
--- a/src/hotspot/share/runtime/sharedRuntime.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp	Thu May 30 13:39:13 2019 +0300
@@ -1314,6 +1314,12 @@
           }
         }
       } else {
+        if (VM_Version::supports_fast_class_init_checks() &&
+            invoke_code == Bytecodes::_invokestatic &&
+            callee_method->needs_clinit_barrier() &&
+            callee != NULL && (callee->is_compiled_by_jvmci() || callee->is_aot())) {
+          return true; // skip patching for JVMCI or AOT code
+        }
         CompiledStaticCall* ssc = caller_nm->compiledStaticCall_before(caller_frame.pc());
         if (ssc->is_clean()) ssc->set(static_call_info);
       }
@@ -1376,12 +1382,20 @@
   }
 #endif
 
-  // Do not patch call site for static call when the class is not
-  // fully initialized.
-  if (invoke_code == Bytecodes::_invokestatic &&
-      !callee_method->method_holder()->is_initialized()) {
-    assert(callee_method->method_holder()->is_linked(), "must be");
-    return callee_method;
+  if (invoke_code == Bytecodes::_invokestatic) {
+    assert(callee_method->method_holder()->is_initialized() ||
+           callee_method->method_holder()->is_reentrant_initialization(thread),
+           "invalid class initialization state for invoke_static");
+    if (!VM_Version::supports_fast_class_init_checks() && callee_method->needs_clinit_barrier()) {
+      // In order to keep class initialization check, do not patch call
+      // site for static call when the class is not fully initialized.
+      // Proper check is enforced by call site re-resolution on every invocation.
+      //
+      // When fast class initialization checks are supported (VM_Version::supports_fast_class_init_checks() == true),
+      // explicit class initialization check is put in nmethod entry (VEP).
+      assert(callee_method->method_holder()->is_linked(), "must be");
+      return callee_method;
+    }
   }
 
   // JSR 292 key invariant:
--- a/src/hotspot/share/runtime/vmStructs.cpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Thu May 30 13:39:13 2019 +0300
@@ -2388,6 +2388,7 @@
   declare_constant(Deoptimization::Reason_profile_predicate)              \
   declare_constant(Deoptimization::Reason_unloaded)                       \
   declare_constant(Deoptimization::Reason_uninitialized)                  \
+  declare_constant(Deoptimization::Reason_initialized)                    \
   declare_constant(Deoptimization::Reason_unreached)                      \
   declare_constant(Deoptimization::Reason_unhandled)                      \
   declare_constant(Deoptimization::Reason_constraint)                     \
--- a/src/hotspot/share/runtime/vm_version.hpp	Thu May 30 11:42:53 2019 +0200
+++ b/src/hotspot/share/runtime/vm_version.hpp	Thu May 30 13:39:13 2019 +0300
@@ -171,6 +171,9 @@
   // Does this CPU support spin wait instruction?
   static bool supports_on_spin_wait() { return false; }
 
+  // Does platform support fast class initialization checks for static methods?
+  static bool supports_fast_class_init_checks() { return false; }
+
   static bool print_matching_lines_from_file(const char* filename, outputStream* st, const char* keywords_to_match[]);
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/clinit/ClassInitBarrier.java	Thu May 30 13:39:13 2019 +0300
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @library /test/lib
+ *
+ * @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* -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:-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: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=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
+ */
+
+import jdk.test.lib.Asserts;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+
+public class ClassInitBarrier {
+    static {
+        System.loadLibrary("ClassInitBarrier");
+
+        if (!init()) {
+            throw new Error("init failed");
+        }
+    }
+
+    static native boolean init();
+
+    static final boolean THROW = Boolean.getBoolean("THROW");
+
+    static class Test {
+        static class A {
+            static {
+                changePhase(Phase.IN_PROGRESS);
+                runTests();      // interpreted mode
+                warmup();        // trigger compilation
+                runTests();      // compiled mode
+
+                ensureBlocked(); // ensure still blocked
+                maybeThrow();    // fail initialization if needed
+
+                changePhase(Phase.FINISHED);
+            }
+
+            static              void staticM(Runnable action) { action.run(); }
+            static synchronized void staticS(Runnable action) { action.run(); }
+            static native       void staticN(Runnable action);
+
+            static int staticF;
+
+            int f;
+            void m() {}
+        }
+
+        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 int  testGetStatic(Runnable action)    { int v = A.staticF; action.run(); return v;   }
+        static void testPutStatic(Runnable action)    { A.staticF = 1;     action.run(); }
+        static A    testNewInstanceA(Runnable action) { A obj = new A();   action.run(); return obj; }
+        static B    testNewInstanceB(Runnable action) { B obj = new B();   action.run(); return obj; }
+
+        static int  testGetField(A recv, Runnable action)      { int v = recv.f; action.run(); return v; }
+        static void testPutField(A recv, Runnable action)      { recv.f = 1;     action.run(); }
+        static void testInvokeVirtual(A recv, Runnable action) { recv.m();       action.run(); }
+
+        static void runTests() {
+            checkBlockingAction(Test::testInvokeStatic);       // invokestatic
+            checkBlockingAction(Test::testInvokeStaticNative); // invokestatic
+            checkBlockingAction(Test::testInvokeStaticSync);   // invokestatic
+            checkBlockingAction(Test::testGetStatic);          // getstatic
+            checkBlockingAction(Test::testPutStatic);          // putstatic
+            checkBlockingAction(Test::testNewInstanceA);       // 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
+        }
+
+        static void warmup() {
+            for (int i = 0; i < 20_000; i++) {
+                testInvokeStatic(      NON_BLOCKING_WARMUP);
+                testInvokeStaticNative(NON_BLOCKING_WARMUP);
+                testInvokeStaticSync(  NON_BLOCKING_WARMUP);
+                testGetStatic(         NON_BLOCKING_WARMUP);
+                testPutStatic(         NON_BLOCKING_WARMUP);
+                testNewInstanceA(      NON_BLOCKING_WARMUP);
+                testNewInstanceB(      NON_BLOCKING_WARMUP);
+
+                testGetField(new B(),      NON_BLOCKING_WARMUP);
+                testPutField(new B(),      NON_BLOCKING_WARMUP);
+                testInvokeVirtual(new B(), NON_BLOCKING_WARMUP);
+            }
+        }
+
+        static void run() {
+            execute(ExceptionInInitializerError.class, () -> triggerInitialization(A.class));
+
+            ensureFinished();
+        }
+    }
+
+    // ============================================================================================================== //
+
+    static void execute(Class<? extends Throwable> expectedExceptionClass, Runnable action) {
+        try {
+            action.run();
+            if (THROW)  throw new AssertionError("no exception thrown");
+        } catch (Throwable e) {
+            if (THROW) {
+                if (e.getClass() == expectedExceptionClass) {
+                    // expected
+                } else {
+                    String msg = String.format("unexpected exception thrown: expected %s, caught %s",
+                            expectedExceptionClass.getName(), e.getClass().getName());
+                    throw new AssertionError(msg, e);
+                }
+            } else {
+                throw new AssertionError("no exception expected", e);
+            }
+        }
+    }
+
+    static final List<Thread> BLOCKED_THREADS = Collections.synchronizedList(new ArrayList<>());
+    static final Consumer<Thread> ON_BLOCK = BLOCKED_THREADS::add;
+
+    static final Map<Thread,Throwable> FAILED_THREADS = Collections.synchronizedMap(new HashMap<>());
+    static final Thread.UncaughtExceptionHandler ON_FAILURE = FAILED_THREADS::put;
+
+    private static void ensureBlocked() {
+        for (Thread thr : BLOCKED_THREADS) {
+            try {
+                thr.join(100);
+                if (!thr.isAlive()) {
+                    dump(thr);
+                    throw new AssertionError("not blocked");
+                }
+            } catch (InterruptedException e) {
+                throw new Error(e);
+            }
+        }
+    }
+
+
+    private static void ensureFinished() {
+        for (Thread thr : BLOCKED_THREADS) {
+            try {
+                thr.join(15_000);
+            } catch (InterruptedException e) {
+                throw new Error(e);
+            }
+            if (thr.isAlive()) {
+                dump(thr);
+                throw new AssertionError(thr + ": still blocked");
+            }
+        }
+        for (Thread thr : BLOCKED_THREADS) {
+            if (THROW) {
+                if (!FAILED_THREADS.containsKey(thr)) {
+                    throw new AssertionError(thr + ": exception not thrown");
+                }
+
+                Throwable ex = FAILED_THREADS.get(thr);
+                if (ex.getClass() != NoClassDefFoundError.class) {
+                    throw new AssertionError(thr + ": wrong exception thrown", ex);
+                }
+            } else {
+                if (FAILED_THREADS.containsKey(thr)) {
+                    Throwable ex = FAILED_THREADS.get(thr);
+                    throw new AssertionError(thr + ": exception thrown", ex);
+                }
+            }
+        }
+        if (THROW) {
+            Asserts.assertEquals(BLOCKING_COUNTER.get(), 0);
+        } else {
+            Asserts.assertEquals(BLOCKING_COUNTER.get(), BLOCKING_ACTIONS.get());
+        }
+
+        dumpInfo();
+    }
+
+    interface TestCase0 {
+        void run(Runnable runnable);
+    }
+
+    interface TestCase1<T> {
+        void run(T arg, Runnable runnable);
+    }
+
+    enum Phase { BEFORE_INIT, IN_PROGRESS, FINISHED, INIT_FAILURE }
+
+    static volatile Phase phase = Phase.BEFORE_INIT;
+
+    static void changePhase(Phase newPhase) {
+        dumpInfo();
+
+        Phase oldPhase = phase;
+        switch (oldPhase) {
+            case BEFORE_INIT:
+                Asserts.assertEquals(NON_BLOCKING_ACTIONS.get(), 0);
+                Asserts.assertEquals(NON_BLOCKING_COUNTER.get(), 0);
+
+                Asserts.assertEquals(BLOCKING_ACTIONS.get(),     0);
+                Asserts.assertEquals(BLOCKING_COUNTER.get(),     0);
+                break;
+            case IN_PROGRESS:
+                Asserts.assertEquals(NON_BLOCKING_COUNTER.get(), NON_BLOCKING_ACTIONS.get());
+
+                Asserts.assertEquals(BLOCKING_COUNTER.get(), 0);
+                break;
+            default: throw new Error("wrong phase transition " + oldPhase);
+        }
+        phase = newPhase;
+    }
+
+    static void dumpInfo() {
+        System.out.println("Phase: " + phase);
+        System.out.println("Non-blocking actions: " + NON_BLOCKING_COUNTER.get() + " / " + NON_BLOCKING_ACTIONS.get());
+        System.out.println("Blocking actions:     " + BLOCKING_COUNTER.get()     + " / " + BLOCKING_ACTIONS.get());
+    }
+
+    static final Runnable NON_BLOCKING_WARMUP = () -> {
+        if (phase != Phase.IN_PROGRESS) {
+            throw new AssertionError("NON_BLOCKING: wrong phase: " + phase);
+        }
+    };
+
+    static Runnable disposableAction(final Phase validPhase, final AtomicInteger invocationCounter, final AtomicInteger actionCounter) {
+        actionCounter.incrementAndGet();
+
+        final AtomicBoolean cnt = new AtomicBoolean(false);
+        return () -> {
+            if (cnt.getAndSet(true)) {
+                throw new Error("repeated invocation");
+            }
+            invocationCounter.incrementAndGet();
+            if (phase != validPhase) {
+                throw new AssertionError("NON_BLOCKING: wrong phase: " + phase);
+            }
+        };
+    }
+
+    @FunctionalInterface
+    interface Factory<V> {
+        V get();
+    }
+
+    static final AtomicInteger NON_BLOCKING_COUNTER = new AtomicInteger(0);
+    static final AtomicInteger NON_BLOCKING_ACTIONS = new AtomicInteger(0);
+    static final Factory<Runnable> NON_BLOCKING = () -> disposableAction(Phase.IN_PROGRESS, NON_BLOCKING_COUNTER, NON_BLOCKING_ACTIONS);
+
+    static final AtomicInteger BLOCKING_COUNTER = new AtomicInteger(0);
+    static final AtomicInteger BLOCKING_ACTIONS = new AtomicInteger(0);
+    static final Factory<Runnable> BLOCKING     = () -> disposableAction(Phase.FINISHED, BLOCKING_COUNTER, BLOCKING_ACTIONS);
+
+    static void checkBlockingAction(TestCase0 r) {
+        r.run(NON_BLOCKING.get()); // same thread
+        checkBlocked(ON_BLOCK, ON_FAILURE, r); // different thread
+    }
+
+    static void checkNonBlockingAction(TestCase0 r) {
+        r.run(NON_BLOCKING.get());
+        checkNotBlocked(r); // different thread
+    }
+
+    static <T> void checkNonBlockingAction(T recv, TestCase1<T> r) {
+        r.run(recv, NON_BLOCKING.get()); // same thread
+        checkNotBlocked((action) -> r.run(recv, action)); // different thread
+    }
+
+    static void triggerInitialization(Class<?> cls) {
+        try {
+            Class<?> loadedClass = Class.forName(cls.getName(), true, cls.getClassLoader());
+            if (loadedClass != cls) {
+                throw new Error("wrong class");
+            }
+        } catch (ClassNotFoundException e) {
+            throw new Error(e);
+        }
+    }
+
+    static void checkBlocked(Consumer<Thread> onBlockHandler, Thread.UncaughtExceptionHandler onException, TestCase0 r) {
+        Thread thr = new Thread(() -> {
+            try {
+                r.run(BLOCKING.get());
+                System.out.println("Thread " + Thread.currentThread() + ": Finished successfully");
+            } catch(Throwable e) {
+                System.out.println("Thread " + Thread.currentThread() + ": Exception thrown: " + e);
+                if (!THROW) {
+                    e.printStackTrace();
+                }
+                throw e;
+            }
+        } );
+        thr.setUncaughtExceptionHandler(onException);
+
+        thr.start();
+        try {
+            thr.join(100);
+
+            dump(thr);
+            if (thr.isAlive()) {
+                onBlockHandler.accept(thr); // blocked
+            } else {
+                throw new AssertionError("not blocked");
+            }
+        } catch (InterruptedException e) {
+            throw new Error(e);
+        }
+    }
+
+    static void checkNotBlocked(TestCase0 r) {
+        Thread thr = new Thread(() -> r.run(NON_BLOCKING.get()));
+
+        thr.start();
+        try {
+            thr.join(15_000);
+            if (thr.isAlive()) {
+                dump(thr);
+                throw new AssertionError("blocked");
+            }
+        } catch (InterruptedException e) {
+            throw new Error(e);
+        }
+    }
+
+    static void maybeThrow() {
+        if (THROW) {
+            changePhase(Phase.INIT_FAILURE);
+            throw new RuntimeException("failed class initialization");
+        }
+    }
+
+    private static void dump(Thread thr) {
+        System.out.println("Thread: " + thr);
+        System.out.println("Thread state: " + thr.getState());
+        if (thr.isAlive()) {
+            for (StackTraceElement frame : thr.getStackTrace()) {
+                System.out.println(frame);
+            }
+        } else {
+            if (FAILED_THREADS.containsKey(thr)) {
+                System.out.println("Failed with an exception: ");
+                FAILED_THREADS.get(thr).toString();
+            } else {
+                System.out.println("Finished successfully");
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        Test.run();
+        System.out.println("TEST PASSED");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/clinit/libClassInitBarrier.cpp	Thu May 30 13:39:13 2019 +0300
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <jni.h>
+
+static jmethodID methodId;
+
+extern "C" {
+    JNIEXPORT jboolean JNICALL Java_ClassInitBarrier_init(JNIEnv* env, jclass cls) {
+        jclass runnable = env->FindClass("java/lang/Runnable");
+        if (runnable == NULL)  return JNI_FALSE;
+
+        methodId = env->GetMethodID(runnable, "run", "()V");
+        if (methodId == 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);
+    }
+}