8224826: Implement fast class initialization checks on PPC64
authormdoerr
Tue, 11 Jun 2019 09:51:33 +0200
changeset 55322 bc5baf205475
parent 55321 ddda023e6f66
child 55323 fc4042870cc4
8224826: Implement fast class initialization checks on PPC64 Reviewed-by: vlivanov, gromero
src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp
src/hotspot/cpu/ppc/interp_masm_ppc.hpp
src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp
src/hotspot/cpu/ppc/macroAssembler_ppc.cpp
src/hotspot/cpu/ppc/macroAssembler_ppc.hpp
src/hotspot/cpu/ppc/ppc.ad
src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp
src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
src/hotspot/cpu/ppc/vm_version_ppc.hpp
--- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp	Tue Jun 11 09:51:33 2019 +0200
@@ -80,7 +80,19 @@
 }
 
 void LIR_Assembler::clinit_barrier(ciMethod* method) {
-  ShouldNotReachHere(); // not implemented
+  assert(!method->holder()->is_not_initialized(), "initialization should have been started");
+
+  Label L_skip_barrier;
+  Register klass = R20;
+
+  metadata2reg(method->holder()->constant_encoding(), klass);
+  __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/);
+
+  __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0);
+  __ mtctr(klass);
+  __ bctr();
+
+  __ bind(L_skip_barrier);
 }
 
 void LIR_Assembler::osr_entry() {
--- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp	Tue Jun 11 09:51:33 2019 +0200
@@ -82,6 +82,8 @@
   // load cpool->resolved_klass_at(index)
   void load_resolved_klass_at_offset(Register Rcpool, Register Roffset, Register Rklass);
 
+  void load_resolved_method_at_index(int byte_no, Register cache, Register method);
+
   void load_receiver(Register Rparam_count, Register Rrecv_dst);
 
   // helpers for expression stack
--- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp	Tue Jun 11 09:51:33 2019 +0200
@@ -516,6 +516,18 @@
   ldx(Rklass, Rklass, Roffset);
 }
 
+void InterpreterMacroAssembler::load_resolved_method_at_index(int byte_no,
+                                                              Register cache,
+                                                              Register method) {
+  const int method_offset = in_bytes(
+    ConstantPoolCache::base_offset() +
+      ((byte_no == TemplateTable::f2_byte)
+       ? ConstantPoolCacheEntry::f2_offset()
+       : ConstantPoolCacheEntry::f1_offset()));
+
+  ld(method, method_offset, cache); // get f1 Method*
+}
+
 // Generate a subtype check: branch to ok_is_subtype if sub_klass is
 // a subtype of super_klass. Blows registers Rsub_klass, tmp1, tmp2.
 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Register Rsuper_klass, Register Rtmp1,
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp	Tue Jun 11 09:51:33 2019 +0200
@@ -2011,6 +2011,35 @@
   bind(L_failure); // Fallthru if not successful.
 }
 
+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;
+  } else if (L_slow_path == NULL) {
+    L_slow_path = &L_fallthrough;
+  }
+
+  // Fast path check: class is fully initialized
+  lbz(R0, in_bytes(InstanceKlass::init_state_offset()), klass);
+  cmpwi(CCR0, R0, InstanceKlass::fully_initialized);
+  beq(CCR0, *L_fast_path);
+
+  // Fast path check: current thread is initializer thread
+  ld(R0, in_bytes(InstanceKlass::init_thread_offset()), klass);
+  cmpd(CCR0, thread, R0);
+  if (L_slow_path == &L_fallthrough) {
+    beq(CCR0, *L_fast_path);
+  } else if (L_fast_path == &L_fallthrough) {
+    bne(CCR0, *L_slow_path);
+  } else {
+    Unimplemented();
+  }
+
+  bind(L_fallthrough);
+}
+
 void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
                                               Register temp_reg,
                                               Label& wrong_method_type) {
@@ -3194,6 +3223,12 @@
   resolve_oop_handle(mirror);
 }
 
+void MacroAssembler::load_method_holder(Register holder, Register method) {
+  ld(holder, in_bytes(Method::const_offset()), method);
+  ld(holder, in_bytes(ConstMethod::constants_offset()), holder);
+  ld(holder, ConstantPool::pool_holder_offset_in_bytes(), holder);
+}
+
 // Clear Array
 // For very short arrays. tmp == R0 is allowed.
 void MacroAssembler::clear_memory_unrolled(Register base_ptr, int cnt_dwords, Register tmp, int offset) {
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp	Tue Jun 11 09:51:33 2019 +0200
@@ -559,6 +559,11 @@
                            Register temp2_reg,
                            Label& L_success);
 
+  void clinit_barrier(Register klass,
+                      Register thread,
+                      Label* L_fast_path = NULL,
+                      Label* L_slow_path = NULL);
+
   // Method handle support (JSR 292).
   void check_method_handle_type(Register mtype_reg, Register mh_reg, Register temp_reg, Label& wrong_method_type);
 
@@ -722,6 +727,7 @@
 
   void resolve_oop_handle(Register result);
   void load_mirror_from_const_method(Register mirror, Register const_method);
+  void load_method_holder(Register holder, Register method);
 
   static int instr_size_for_decode_klass_not_null();
   void decode_klass_not_null(Register dst, Register src = noreg);
--- a/src/hotspot/cpu/ppc/ppc.ad	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/ppc.ad	Tue Jun 11 09:51:33 2019 +0200
@@ -1400,6 +1400,24 @@
     ___(mflr) mflr(return_pc);
   }
 
+  if (C->clinit_barrier_on_entry()) {
+    assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");
+
+    Label L_skip_barrier;
+    Register klass = toc_temp;
+
+    // Notify OOP recorder (don't need the relocation)
+    AddressLiteral md = __ constant_metadata_address(C->method()->holder()->constant_encoding());
+    __ load_const_optimized(klass, md.value(), R0);
+    __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0);
+    __ mtctr(klass);
+    __ bctr();
+
+    __ bind(L_skip_barrier);
+  }
+
   // Calls to C2R adapters often do not accept exceptional returns.
   // We require that their callers must bang for them. But be
   // careful, because some VM calls (such as call site linkage) can
--- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp	Tue Jun 11 09:51:33 2019 +0200
@@ -1274,7 +1274,30 @@
 
   // entry: c2i
 
-  c2i_entry = gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, call_interpreter, ientry);
+  c2i_entry = __ pc();
+
+  // Class initialization barrier for static methods
+  if (VM_Version::supports_fast_class_init_checks()) {
+    Label L_skip_barrier;
+
+    { // Bypass the barrier for non-static methods
+      __ lwz(R0, in_bytes(Method::access_flags_offset()), R19_method);
+      __ andi_(R0, R0, JVM_ACC_STATIC);
+      __ beq(CCR0, L_skip_barrier); // non-static
+    }
+
+    Register klass = R11_scratch1;
+    __ load_method_holder(klass, R19_method);
+    __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0);
+    __ mtctr(klass);
+    __ bctr();
+
+    __ bind(L_skip_barrier);
+  }
+
+  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);
 }
@@ -2106,6 +2129,21 @@
     __ tabort_();
   }
 
+  if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
+    Label L_skip_barrier;
+    Register klass = r_temp_1;
+    // Notify OOP recorder (don't need the relocation)
+    AddressLiteral md = __ constant_metadata_address(method->method_holder());
+    __ load_const_optimized(klass, md.value(), R0);
+    __ clinit_barrier(klass, R16_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub(), R0);
+    __ mtctr(klass);
+    __ bctr();
+
+    __ bind(L_skip_barrier);
+  }
+
   __ save_LR_CR(r_temp_1);
   __ generate_stack_overflow_check(frame_size_in_bytes); // Check before creating frame.
   __ mr(r_callers_sp, R1_SP);                            // Remember frame pointer.
--- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp	Tue Jun 11 09:51:33 2019 +0200
@@ -2232,7 +2232,7 @@
 void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register Rscratch, size_t index_size) {
 
   __ get_cache_and_index_at_bcp(Rcache, 1, index_size);
-  Label Lresolved, Ldone;
+  Label Lresolved, Ldone, L_clinit_barrier_slow;
 
   Bytecodes::Code code = bytecode();
   switch (code) {
@@ -2253,6 +2253,9 @@
   __ cmpdi(CCR0, Rscratch, (int)code);
   __ beq(CCR0, Lresolved);
 
+  // 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);
   __ li(R4_ARG2, code);
   __ call_VM(noreg, entry, R4_ARG2, true);
@@ -2263,6 +2266,17 @@
 
   __ bind(Lresolved);
   __ isync(); // Order load wrt. succeeding loads.
+
+  // Class initialization barrier for static methods
+  if (VM_Version::supports_fast_class_init_checks() && bytecode() == Bytecodes::_invokestatic) {
+    const Register method = Rscratch;
+    const Register klass  = Rscratch;
+
+    __ load_resolved_method_at_index(byte_no, Rcache, method);
+    __ load_method_holder(klass, method);
+    __ clinit_barrier(klass, R16_thread, NULL /*L_fast_path*/, &L_clinit_barrier_slow);
+  }
+
   __ bind(Ldone);
 }
 
@@ -2329,7 +2343,7 @@
     // Already resolved.
     __ get_cache_and_index_at_bcp(Rcache, 1);
   } else {
-    resolve_cache_and_index(byte_no, Rcache, R0, is_invokedynamic ? sizeof(u4) : sizeof(u2));
+    resolve_cache_and_index(byte_no, Rcache, /* temp */ Rmethod, is_invokedynamic ? sizeof(u4) : sizeof(u2));
   }
 
   __ ld(Rmethod, method_offset, Rcache);
@@ -3634,9 +3648,7 @@
   // Find entry point to call.
 
   // Get declaring interface class from method
-  __ ld(Rinterface_klass, in_bytes(Method::const_offset()), Rmethod);
-  __ ld(Rinterface_klass, in_bytes(ConstMethod::constants_offset()), Rinterface_klass);
-  __ ld(Rinterface_klass, ConstantPool::pool_holder_offset_in_bytes(), Rinterface_klass);
+  __ load_method_holder(Rinterface_klass, Rmethod);
 
   // Get itable index from method
   __ lwa(Rindex, in_bytes(Method::itable_index_offset()), Rmethod);
--- a/src/hotspot/cpu/ppc/vm_version_ppc.hpp	Tue Jun 11 09:27:51 2019 +0200
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.hpp	Tue Jun 11 09:51:33 2019 +0200
@@ -95,6 +95,9 @@
   // Override Abstract_VM_Version implementation
   static bool use_biased_locking();
 
+  // PPC64 supports fast class initialization checks for static methods.
+  static bool supports_fast_class_init_checks() { return true; }
+
   static bool is_determine_features_test_running() { return _is_determine_features_test_running; }
   // CPU instruction support
   static bool has_fsqrt()   { return (_features & fsqrt_m) != 0; }