8224827: Implement fast class initialization checks on s390
authormdoerr
Wed, 12 Jun 2019 14:22:04 +0200
changeset 55343 03d417fd7d9a
parent 55342 596ae6c3ef6f
child 55344 0c20dbc3d547
8224827: Implement fast class initialization checks on s390 Reviewed-by: vlivanov, gromero
src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp
src/hotspot/cpu/s390/interp_masm_s390.cpp
src/hotspot/cpu/s390/interp_masm_s390.hpp
src/hotspot/cpu/s390/macroAssembler_s390.cpp
src/hotspot/cpu/s390/macroAssembler_s390.hpp
src/hotspot/cpu/s390/s390.ad
src/hotspot/cpu/s390/sharedRuntime_s390.cpp
src/hotspot/cpu/s390/templateTable_s390.cpp
src/hotspot/cpu/s390/vm_version_s390.hpp
--- a/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/c1_LIRAssembler_s390.cpp	Wed Jun 12 14:22:04 2019 +0200
@@ -82,7 +82,18 @@
 }
 
 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 = Z_R1_scratch;
+
+  metadata2reg(method->holder()->constant_encoding(), klass);
+  __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/);
+
+  __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub());
+  __ z_br(klass);
+
+  __ bind(L_skip_barrier);
 }
 
 void LIR_Assembler::osr_entry() {
--- a/src/hotspot/cpu/s390/interp_masm_s390.cpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/interp_masm_s390.cpp	Wed Jun 12 14:22:04 2019 +0200
@@ -414,6 +414,19 @@
   BLOCK_COMMENT("}");
 }
 
+void InterpreterMacroAssembler::load_resolved_method_at_index(int byte_no,
+                                                              Register cache,
+                                                              Register cpe_offset,
+                                                              Register method) {
+  const int method_offset = in_bytes(
+    ConstantPoolCache::base_offset() +
+      ((byte_no == TemplateTable::f2_byte)
+       ? ConstantPoolCacheEntry::f2_offset()
+       : ConstantPoolCacheEntry::f1_offset()));
+
+  z_lg(method, Address(cache, cpe_offset, method_offset)); // get f1 Method*
+}
+
 // Generate a subtype check: branch to ok_is_subtype if sub_klass is
 // a subtype of super_klass. Blows registers Rsuper_klass, Rsub_klass, tmp1, tmp2.
 void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass,
--- a/src/hotspot/cpu/s390/interp_masm_s390.hpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/interp_masm_s390.hpp	Wed Jun 12 14:22:04 2019 +0200
@@ -120,6 +120,8 @@
   // load cpool->resolved_klass_at(index)
   void load_resolved_klass_at_offset(Register cpool, Register offset, Register iklass);
 
+  void load_resolved_method_at_index(int byte_no, Register cache, Register cpe_offset, Register method);
+
   // Pop topmost element from stack. It just disappears. Useful if
   // consumed previously by access via stackTop().
   void popx(int len);
--- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp	Wed Jun 12 14:22:04 2019 +0200
@@ -3130,6 +3130,33 @@
   BLOCK_COMMENT("} check_klass_subtype");
 }
 
+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
+  z_cli(Address(klass, InstanceKlass::init_state_offset()), InstanceKlass::fully_initialized);
+  z_bre(*L_fast_path);
+
+  // Fast path check: current thread is initializer thread
+  z_cg(thread, Address(klass, InstanceKlass::init_thread_offset()));
+  if (L_slow_path == &L_fallthrough) {
+    z_bre(*L_fast_path);
+  } else if (L_fast_path == &L_fallthrough) {
+    z_brne(*L_slow_path);
+  } else {
+    Unimplemented();
+  }
+
+  bind(L_fallthrough);
+}
+
 // Increment a counter at counter_address when the eq condition code is
 // set. Kills registers tmp1_reg and tmp2_reg and preserves the condition code.
 void MacroAssembler::increment_counter_eq(address counter_address, Register tmp1_reg, Register tmp2_reg) {
@@ -4346,6 +4373,12 @@
   resolve_oop_handle(mirror);
 }
 
+void MacroAssembler::load_method_holder(Register holder, Register method) {
+  mem2reg_opt(holder, Address(method, Method::const_offset()));
+  mem2reg_opt(holder, Address(holder, ConstMethod::constants_offset()));
+  mem2reg_opt(holder, Address(holder, ConstantPool::pool_holder_offset_in_bytes()));
+}
+
 //---------------------------------------------------------------
 //---  Operations on arrays.
 //---------------------------------------------------------------
--- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp	Wed Jun 12 14:22:04 2019 +0200
@@ -713,6 +713,11 @@
                            Register temp2_reg,
                            Label&   L_success);
 
+  void clinit_barrier(Register klass,
+                      Register thread,
+                      Label* L_fast_path = NULL,
+                      Label* L_slow_path = NULL);
+
   // Increment a counter at counter_address when the eq condition code is set.
   // Kills registers tmp1_reg and tmp2_reg and preserves the condition code.
   void increment_counter_eq(address counter_address, Register tmp1_reg, Register tmp2_reg);
@@ -824,6 +829,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);
 
   //--------------------------
   //---  Operations on arrays.
--- a/src/hotspot/cpu/s390/s390.ad	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/s390.ad	Wed Jun 12 14:22:04 2019 +0200
@@ -867,6 +867,23 @@
 
   assert(framesize % wordSize == 0, "must preserve wordSize alignment");
 
+  if (C->clinit_barrier_on_entry()) {
+    assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started");
+
+    Label L_skip_barrier;
+    Register klass = Z_R1_scratch;
+
+    // Notify OOP recorder (don't need the relocation)
+    AddressLiteral md = __ constant_metadata_address(C->method()->holder()->constant_encoding());
+    __ load_const_optimized(klass, md.value());
+    __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub());
+    __ z_br(klass);
+
+    __ 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/s390/sharedRuntime_s390.cpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp	Wed Jun 12 14:22:04 2019 +0200
@@ -1832,6 +1832,20 @@
   //---------------------------------------------------------------------
   wrapper_VEPStart = __ offset();
 
+  if (VM_Version::supports_fast_class_init_checks() && method->needs_clinit_barrier()) {
+    Label L_skip_barrier;
+    Register klass = Z_R1_scratch;
+    // Notify OOP recorder (don't need the relocation)
+    AddressLiteral md = __ constant_metadata_address(method->method_holder());
+    __ load_const_optimized(klass, md.value());
+    __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub());
+    __ z_br(klass);
+
+    __ bind(L_skip_barrier);
+  }
+
   __ save_return_pc();
   __ generate_stack_overflow_check(frame_size_in_bytes);  // Check before creating frame.
 #ifndef USE_RESIZE_FRAME
@@ -2696,8 +2710,28 @@
     // Fallthru to VEP. Duplicate LTG, but saved taken branch.
   }
 
-  address c2i_entry;
-  c2i_entry = gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
+  address 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
+      __ testbit(Address(Z_method, Method::access_flags_offset()), JVM_ACC_STATIC_BIT);
+      __ z_bfalse(L_skip_barrier); // non-static
+    }
+
+    Register klass = Z_R11;
+    __ load_method_holder(klass, Z_method);
+    __ clinit_barrier(klass, Z_thread, &L_skip_barrier /*L_fast_path*/);
+
+    __ load_const_optimized(klass, SharedRuntime::get_handle_wrong_method_stub());
+    __ z_br(klass);
+
+    __ bind(L_skip_barrier);
+  }
+
+  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);
 }
--- a/src/hotspot/cpu/s390/templateTable_s390.cpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/templateTable_s390.cpp	Wed Jun 12 14:22:04 2019 +0200
@@ -2404,14 +2404,14 @@
 // NOTE: Cpe_offset is already computed as byte offset, so we must not
 // shift it afterwards!
 void TemplateTable::resolve_cache_and_index(int byte_no,
-                                            Register Rcache,
+                                            Register cache,
                                             Register cpe_offset,
                                             size_t index_size) {
   BLOCK_COMMENT("resolve_cache_and_index {");
-  NearLabel      resolved;
+  NearLabel      resolved, clinit_barrier_slow;
   const Register bytecode_in_cpcache = Z_R1_scratch;
   const int      total_f1_offset = in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::f1_offset());
-  assert_different_registers(Rcache, cpe_offset, bytecode_in_cpcache);
+  assert_different_registers(cache, cpe_offset, bytecode_in_cpcache);
 
   Bytecodes::Code code = bytecode();
   switch (code) {
@@ -2423,19 +2423,32 @@
 
   {
     assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range");
-    __ get_cache_and_index_and_bytecode_at_bcp(Rcache, cpe_offset, bytecode_in_cpcache, byte_no, 1, index_size);
+    __ get_cache_and_index_and_bytecode_at_bcp(cache, cpe_offset, bytecode_in_cpcache, byte_no, 1, index_size);
     // Have we resolved this bytecode?
     __ compare32_and_branch(bytecode_in_cpcache, (int)code, Assembler::bcondEqual, resolved);
   }
 
   // Resolve first time through.
+  // Class initialization barrier slow path lands here as well.
+  __ bind(clinit_barrier_slow);
   address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_from_cache);
   __ load_const_optimized(Z_ARG2, (int) code);
   __ call_VM(noreg, entry, Z_ARG2);
 
   // Update registers with resolved info.
-  __ get_cache_and_index_at_bcp(Rcache, cpe_offset, 1, index_size);
+  __ get_cache_and_index_at_bcp(cache, cpe_offset, 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 = Z_R1_scratch;
+    const Register klass  = Z_R1_scratch;
+
+    __ load_resolved_method_at_index(byte_no, cache, cpe_offset, method);
+    __ load_method_holder(klass, method);
+    __ clinit_barrier(klass, Z_thread, NULL /*L_fast_path*/, &clinit_barrier_slow);
+  }
+
   BLOCK_COMMENT("} resolve_cache_and_index");
 }
 
@@ -3664,9 +3677,7 @@
   // Find entry point to call.
 
   // Get declaring interface class from method
-  __ z_lg(interface, Address(method, Method::const_offset()));
-  __ z_lg(interface, Address(interface, ConstMethod::constants_offset()));
-  __ z_lg(interface, Address(interface, ConstantPool::pool_holder_offset_in_bytes()));
+  __ load_method_holder(interface, method);
 
   // Get itable index from method
   Register index   = receiver,
--- a/src/hotspot/cpu/s390/vm_version_s390.hpp	Wed Jun 12 14:21:36 2019 +0200
+++ b/src/hotspot/cpu/s390/vm_version_s390.hpp	Wed Jun 12 14:22:04 2019 +0200
@@ -349,6 +349,9 @@
   // Override Abstract_VM_Version implementation
   static void print_platform_virtualization_info(outputStream*);
 
+  // s390 supports fast class initialization checks for static methods.
+  static bool supports_fast_class_init_checks() { return true; }
+
   // CPU feature query functions
   static const char* get_model_string()       { return _model_string; }
   static bool has_StoreFacilityListExtended() { return  (_features[0] & StoreFacilityListExtendedMask) == StoreFacilityListExtendedMask; }