8224827: Implement fast class initialization checks on s390
Reviewed-by: vlivanov, gromero
--- 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; }