8319947: Recursive lightweight locking: s390x implementation

Reviewed-by: aboldtch, lucy
This commit is contained in:
Amit Kumar 2024-06-28 06:43:32 +00:00
parent c47a0e005e
commit d457609f70
9 changed files with 490 additions and 128 deletions

@ -67,9 +67,6 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox
verify_oop(Roop, FILE_AND_LINE);
// Load object header.
z_lg(Rmark, Address(Roop, hdr_offset));
// Save object being locked into the BasicObjectLock...
z_stg(Roop, Address(Rbox, BasicObjectLock::obj_offset()));
@ -85,6 +82,10 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox
lightweight_lock(Roop, Rmark, tmp, slow_case);
} else if (LockingMode == LM_LEGACY) {
NearLabel done;
// Load object header.
z_lg(Rmark, Address(Roop, hdr_offset));
// and mark it as unlocked.
z_oill(Rmark, markWord::unlocked_value);
// Save unlocked object header into the displaced header location on the stack.
@ -141,12 +142,7 @@ void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rb
verify_oop(Roop, FILE_AND_LINE);
if (LockingMode == LM_LIGHTWEIGHT) {
const Register tmp = Z_R1_scratch;
z_lg(Rmark, Address(Roop, hdr_offset));
z_lgr(tmp, Rmark);
z_nill(tmp, markWord::monitor_value);
branch_optimized(Assembler::bcondNotZero, slow_case);
lightweight_unlock(Roop, Rmark, tmp, slow_case);
lightweight_unlock(Roop, Rmark, Z_R1_scratch, slow_case);
} else if (LockingMode == LM_LEGACY) {
// Test if object header is pointing to the displaced header, and if so, restore
// the displaced header in the object. If the object header is not pointing to

@ -1,6 +1,6 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2022 SAP SE. All rights reserved.
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024 SAP SE. 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
@ -33,6 +33,15 @@
#define BLOCK_COMMENT(str) block_comment(str)
#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register temp1, Register temp2) {
compiler_fast_lock_lightweight_object(obj, temp1, temp2);
}
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register temp1, Register temp2) {
compiler_fast_unlock_lightweight_object(obj, temp1, temp2);
}
//------------------------------------------------------
// Special String Intrinsics. Implementation
//------------------------------------------------------

@ -1,6 +1,6 @@
/*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2022 SAP SE. All rights reserved.
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2024 SAP SE. 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
@ -29,6 +29,10 @@
// C2_MacroAssembler contains high-level macros for C2
public:
// Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in s390.ad file.
void fast_lock_lightweight(Register obj, Register box, Register temp1, Register temp2);
void fast_unlock_lightweight(Register obj, Register box, Register temp1, Register temp2);
//-------------------------------------------
// Special String Intrinsics Implementation.
//-------------------------------------------

@ -1005,9 +1005,6 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
// markWord header = obj->mark().set_unlocked();
// Load markWord from object into header.
z_lg(header, hdr_offset, object);
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp, object);
testbit(Address(tmp, Klass::access_flags_offset()), exact_log2(JVM_ACC_IS_VALUE_BASED_CLASS));
@ -1015,9 +1012,12 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
}
if (LockingMode == LM_LIGHTWEIGHT) {
lightweight_lock(object, /* mark word */ header, tmp, slow_case);
lightweight_lock(object, header, tmp, slow_case);
} else if (LockingMode == LM_LEGACY) {
// Load markWord from object into header.
z_lg(header, hdr_offset, object);
// Set header to be (markWord of object | UNLOCK_VALUE).
// This will not change anything if it was unlocked before.
z_oill(header, markWord::unlocked_value);
@ -1153,26 +1153,8 @@ void InterpreterMacroAssembler::unlock_object(Register monitor, Register object)
// If we still have a lightweight lock, unlock the object and be done.
if (LockingMode == LM_LIGHTWEIGHT) {
// Check for non-symmetric locking. This is allowed by the spec and the interpreter
// must handle it.
Register tmp = current_header;
// First check for lock-stack underflow.
z_lgf(tmp, Address(Z_thread, JavaThread::lock_stack_top_offset()));
compareU32_and_branch(tmp, (unsigned)LockStack::start_offset(), Assembler::bcondNotHigh, slow_case);
// Then check if the top of the lock-stack matches the unlocked object.
z_aghi(tmp, -oopSize);
z_lg(tmp, Address(Z_thread, tmp));
compare64_and_branch(tmp, object, Assembler::bcondNotEqual, slow_case);
z_lg(header, Address(object, hdr_offset));
z_lgr(tmp, header);
z_nill(tmp, markWord::monitor_value);
z_brne(slow_case);
lightweight_unlock(object, header, tmp, slow_case);
lightweight_unlock(object, header, current_header, slow_case);
z_bru(done);
} else {

@ -3190,16 +3190,20 @@ void MacroAssembler::increment_counter_eq(address counter_address, Register tmp1
bind(l);
}
// "The box" is the space on the stack where we copy the object mark.
void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Register temp1, Register temp2) {
assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_lock_lightweight");
assert_different_registers(oop, box, temp1, temp2);
Register displacedHeader = temp1;
Register currentHeader = temp1;
Register temp = temp2;
Register currentHeader = temp1;
Register temp = temp2;
NearLabel done, object_has_monitor;
const int hdr_offset = oopDesc::mark_offset_in_bytes();
assert_different_registers(temp1, temp2, oop, box);
BLOCK_COMMENT("compiler_fast_lock_object {");
// Load markWord from oop into mark.
@ -3207,8 +3211,10 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(temp, oop);
testbit(Address(temp, Klass::access_flags_offset()), exact_log2(JVM_ACC_IS_VALUE_BASED_CLASS));
z_btrue(done);
z_l(temp, Address(temp, Klass::access_flags_offset()));
assert((JVM_ACC_IS_VALUE_BASED_CLASS & 0xFFFF) == 0, "or change following instruction");
z_nilh(temp, JVM_ACC_IS_VALUE_BASED_CLASS >> 16);
z_brne(done);
}
// Handle existing monitor.
@ -3222,7 +3228,8 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis
// From loading the markWord, we know that oop != nullptr
z_ltgr(oop, oop);
z_bru(done);
} else if (LockingMode == LM_LEGACY) {
} else {
assert(LockingMode == LM_LEGACY, "must be");
// Set mark to markWord | markWord::unlocked_value.
z_oill(displacedHeader, markWord::unlocked_value);
@ -3251,10 +3258,6 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis
z_stg(currentHeader/*==0 or not 0*/, BasicLock::displaced_header_offset_in_bytes(), box);
z_bru(done);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
lightweight_lock(oop, displacedHeader, temp, done);
z_bru(done);
}
@ -3270,10 +3273,9 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis
// Otherwise, register zero is filled with the current owner.
z_lghi(zero, 0);
z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor_tagged);
if (LockingMode != LM_LIGHTWEIGHT) {
// Store a non-null value into the box.
z_stg(box, BasicLock::displaced_header_offset_in_bytes(), box);
}
// Store a non-null value into the box.
z_stg(box, BasicLock::displaced_header_offset_in_bytes(), box);
z_bre(done); // acquired the lock for the first time.
@ -3295,14 +3297,16 @@ void MacroAssembler::compiler_fast_lock_object(Register oop, Register box, Regis
}
void MacroAssembler::compiler_fast_unlock_object(Register oop, Register box, Register temp1, Register temp2) {
assert(LockingMode != LM_LIGHTWEIGHT, "uses fast_unlock_lightweight");
assert_different_registers(oop, box, temp1, temp2);
Register displacedHeader = temp1;
Register currentHeader = temp2;
Register temp = temp1;
Register currentHeader = temp2;
Register temp = temp1;
const int hdr_offset = oopDesc::mark_offset_in_bytes();
assert_different_registers(temp1, temp2, oop, box);
Label done, object_has_monitor, not_recursive;
BLOCK_COMMENT("compiler_fast_unlock_object {");
@ -3326,18 +3330,14 @@ void MacroAssembler::compiler_fast_unlock_object(Register oop, Register box, Reg
// Set NE to indicate 'failure' -> take slow-path
z_ltgr(oop, oop);
z_bru(done);
} else if (LockingMode == LM_LEGACY) {
} else {
assert(LockingMode == LM_LEGACY, "must be");
// Check if it is still a lightweight lock, this is true if we see
// the stack address of the basicLock in the markWord of the object
// copy box to currentHeader such that csg does not kill it.
z_lgr(currentHeader, box);
z_csg(currentHeader, displacedHeader, hdr_offset, oop);
z_bru(done); // csg sets CR as desired.
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
lightweight_unlock(oop, currentHeader, displacedHeader, done);
z_bru(done);
}
// In case of LM_LIGHTWEIGHT, we may reach here with (temp & ObjectMonitor::ANONYMOUS_OWNER) != 0.
@ -5705,103 +5705,426 @@ SkipIfEqual::~SkipIfEqual() {
}
// Implements lightweight-locking.
// Branches to slow upon failure to lock the object.
// Falls through upon success.
//
// - obj: the object to be locked, contents preserved.
// - hdr: the header, already loaded from obj, contents destroyed.
// - temp1, temp2: temporary registers, contents destroyed.
// Note: make sure Z_R1 is not manipulated here when C2 compiler is in play
void MacroAssembler::lightweight_lock(Register obj, Register hdr, Register temp, Label& slow_case) {
void MacroAssembler::lightweight_lock(Register obj, Register temp1, Register temp2, Label& slow) {
assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking");
assert_different_registers(obj, hdr, temp);
assert_different_registers(obj, temp1, temp2);
Label push;
const Register top = temp1;
const Register mark = temp2;
const int mark_offset = oopDesc::mark_offset_in_bytes();
const ByteSize ls_top_offset = JavaThread::lock_stack_top_offset();
// Preload the markWord. It is important that this is the first
// instruction emitted as it is part of C1's null check semantics.
z_lg(mark, Address(obj, mark_offset));
// First we need to check if the lock-stack has room for pushing the object reference.
z_lgf(temp, Address(Z_thread, JavaThread::lock_stack_top_offset()));
z_lgf(top, Address(Z_thread, ls_top_offset));
compareU32_and_branch(temp, (unsigned)LockStack::end_offset()-1, bcondHigh, slow_case);
compareU32_and_branch(top, (unsigned)LockStack::end_offset(), bcondNotLow, slow);
// attempting a lightweight_lock
// Load (object->mark() | 1) into hdr
z_oill(hdr, markWord::unlocked_value);
// The underflow check is elided. The recursive check will always fail
// when the lock stack is empty because of the _bad_oop_sentinel field.
z_lgr(temp, hdr);
// Check for recursion:
z_aghi(top, -oopSize);
z_cg(obj, Address(Z_thread, top));
z_bre(push);
// Clear lock-bits from hdr (locked state)
z_xilf(temp, markWord::unlocked_value);
// Check header for monitor (0b10).
z_tmll(mark, markWord::monitor_value);
branch_optimized(bcondNotAllZero, slow);
z_csg(hdr, temp, oopDesc::mark_offset_in_bytes(), obj);
branch_optimized(Assembler::bcondNotEqual, slow_case);
{ // Try to lock. Transition lock bits 0b01 => 0b00
const Register locked_obj = top;
z_oill(mark, markWord::unlocked_value);
z_lgr(locked_obj, mark);
// Clear lock-bits from locked_obj (locked state)
z_xilf(locked_obj, markWord::unlocked_value);
z_csg(mark, locked_obj, mark_offset, obj);
branch_optimized(Assembler::bcondNotEqual, slow);
}
bind(push);
// After successful lock, push object on lock-stack
z_lgf(temp, Address(Z_thread, JavaThread::lock_stack_top_offset()));
z_stg(obj, Address(Z_thread, temp));
z_ahi(temp, oopSize);
z_st(temp, Address(Z_thread, JavaThread::lock_stack_top_offset()));
// as locking was successful, set CC to EQ
z_cr(temp, temp);
z_lgf(top, Address(Z_thread, ls_top_offset));
z_stg(obj, Address(Z_thread, top));
z_alsi(in_bytes(ls_top_offset), Z_thread, oopSize);
}
// Implements lightweight-unlocking.
// Branches to slow upon failure.
// Falls through upon success.
//
// - obj: the object to be unlocked
// - hdr: the (pre-loaded) header of the object, will be destroyed
// - temp1, temp2: temporary registers, will be destroyed
// - Z_R1_scratch: will be killed in case of Interpreter & C1 Compiler
void MacroAssembler::lightweight_unlock(Register obj, Register hdr, Register tmp, Label& slow) {
void MacroAssembler::lightweight_unlock(Register obj, Register temp1, Register temp2, Label& slow) {
assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking");
assert_different_registers(obj, hdr, tmp);
assert_different_registers(obj, temp1, temp2);
Label unlocked, push_and_slow;
const Register mark = temp1;
const Register top = temp2;
const int mark_offset = oopDesc::mark_offset_in_bytes();
const ByteSize ls_top_offset = JavaThread::lock_stack_top_offset();
#ifdef ASSERT
{
// Check that hdr is lightweight-locked.
Label hdr_ok;
z_lgr(tmp, hdr);
z_nill(tmp, markWord::lock_mask_in_place);
z_bre(hdr_ok);
stop("Header is not lightweight-locked");
bind(hdr_ok);
}
{
// The following checks rely on the fact that LockStack is only ever modified by
// its owning thread, even if the lock got inflated concurrently; removal of LockStack
// entries after inflation will happen delayed in that case.
// Check for lock-stack underflow.
Label stack_ok;
z_lgf(tmp, Address(Z_thread, JavaThread::lock_stack_top_offset()));
compareU32_and_branch(tmp, (unsigned)LockStack::start_offset(), Assembler::bcondHigh, stack_ok);
NearLabel stack_ok;
z_lgf(top, Address(Z_thread, ls_top_offset));
compareU32_and_branch(top, (unsigned)LockStack::start_offset(), bcondNotLow, stack_ok);
stop("Lock-stack underflow");
bind(stack_ok);
}
{
// Check if the top of the lock-stack matches the unlocked object.
Label tos_ok;
z_aghi(tmp, -oopSize);
z_lg(tmp, Address(Z_thread, tmp));
compare64_and_branch(tmp, obj, Assembler::bcondEqual, tos_ok);
stop("Top of lock-stack does not match the unlocked object");
bind(tos_ok);
}
#endif // ASSERT
z_lgr(tmp, hdr);
z_oill(tmp, markWord::unlocked_value);
z_csg(hdr, tmp, oopDesc::mark_offset_in_bytes(), obj);
branch_optimized(Assembler::bcondNotEqual, slow);
// Check if obj is top of lock-stack.
z_lgf(top, Address(Z_thread, ls_top_offset));
z_aghi(top, -oopSize);
z_cg(obj, Address(Z_thread, top));
branch_optimized(bcondNotEqual, slow);
// After successful unlock, pop object from lock-stack
// pop object from lock-stack
#ifdef ASSERT
z_lgf(tmp, Address(Z_thread, JavaThread::lock_stack_top_offset()));
z_aghi(tmp, -oopSize);
z_agr(tmp, Z_thread);
z_xc(0, oopSize-1, tmp, 0, tmp); // wipe out lock-stack entry
const Register temp_top = temp1; // mark is not yet loaded, but be careful
z_agrk(temp_top, top, Z_thread);
z_xc(0, oopSize-1, temp_top, 0, temp_top); // wipe out lock-stack entry
#endif // ASSERT
z_alsi(in_bytes(ls_top_offset), Z_thread, -oopSize); // pop object
// The underflow check is elided. The recursive check will always fail
// when the lock stack is empty because of the _bad_oop_sentinel field.
// Check if recursive. (this is a check for the 2nd object on the stack)
z_aghi(top, -oopSize);
z_cg(obj, Address(Z_thread, top));
branch_optimized(bcondEqual, unlocked);
// Not recursive. Check header for monitor (0b10).
z_lg(mark, Address(obj, mark_offset));
z_tmll(mark, markWord::monitor_value);
z_brnaz(push_and_slow);
#ifdef ASSERT
// Check header not unlocked (0b01).
NearLabel not_unlocked;
z_tmll(mark, markWord::unlocked_value);
z_braz(not_unlocked);
stop("lightweight_unlock already unlocked");
bind(not_unlocked);
#endif // ASSERT
{ // Try to unlock. Transition lock bits 0b00 => 0b01
Register unlocked_obj = top;
z_lgr(unlocked_obj, mark);
z_oill(unlocked_obj, markWord::unlocked_value);
z_csg(mark, unlocked_obj, mark_offset, obj);
branch_optimized(Assembler::bcondEqual, unlocked);
}
bind(push_and_slow);
// Restore lock-stack and handle the unlock in runtime.
z_lgf(top, Address(Z_thread, ls_top_offset));
DEBUG_ONLY(z_stg(obj, Address(Z_thread, top));)
z_alsi(in_bytes(ls_top_offset), Z_thread, oopSize);
// set CC to NE
z_ltgr(obj, obj); // object shouldn't be null at this point
branch_optimized(bcondAlways, slow);
bind(unlocked);
}
void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Register tmp1, Register tmp2) {
assert_different_registers(obj, tmp1, tmp2);
// Handle inflated monitor.
NearLabel inflated;
// Finish fast lock successfully. MUST reach to with flag == NE
NearLabel locked;
// Finish fast lock unsuccessfully. MUST branch to with flag == EQ
NearLabel slow_path;
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(tmp1, obj);
z_l(tmp1, Address(tmp1, Klass::access_flags_offset()));
assert((JVM_ACC_IS_VALUE_BASED_CLASS & 0xFFFF) == 0, "or change following instruction");
z_nilh(tmp1, JVM_ACC_IS_VALUE_BASED_CLASS >> 16);
z_brne(slow_path);
}
const Register mark = tmp1;
const int mark_offset = oopDesc::mark_offset_in_bytes();
const ByteSize ls_top_offset = JavaThread::lock_stack_top_offset();
BLOCK_COMMENT("compiler_fast_lightweight_locking {");
{ // lightweight locking
// Push lock to the lock stack and finish successfully. MUST reach to with flag == EQ
NearLabel push;
const Register top = tmp2;
// Check if lock-stack is full.
z_lgf(top, Address(Z_thread, ls_top_offset));
compareU32_and_branch(top, (unsigned) LockStack::end_offset() - 1, bcondHigh, slow_path);
// The underflow check is elided. The recursive check will always fail
// when the lock stack is empty because of the _bad_oop_sentinel field.
// Check if recursive.
z_aghi(top, -oopSize);
z_cg(obj, Address(Z_thread, top));
z_bre(push);
// Check for monitor (0b10)
z_lg(mark, Address(obj, mark_offset));
z_tmll(mark, markWord::monitor_value);
z_brnaz(inflated);
// not inflated
{ // Try to lock. Transition lock bits 0b01 => 0b00
assert(mark_offset == 0, "required to avoid a lea");
const Register locked_obj = top;
z_oill(mark, markWord::unlocked_value);
z_lgr(locked_obj, mark);
// Clear lock-bits from locked_obj (locked state)
z_xilf(locked_obj, markWord::unlocked_value);
z_csg(mark, locked_obj, mark_offset, obj);
branch_optimized(Assembler::bcondNotEqual, slow_path);
}
bind(push);
// After successful lock, push object on lock-stack.
z_lgf(top, Address(Z_thread, ls_top_offset));
z_stg(obj, Address(Z_thread, top));
z_alsi(in_bytes(ls_top_offset), Z_thread, oopSize);
z_cgr(obj, obj); // set the CC to EQ, as it could be changed by alsi
z_bru(locked);
}
BLOCK_COMMENT("} compiler_fast_lightweight_locking");
BLOCK_COMMENT("handle_inflated_monitor_lightweight_locking {");
{ // Handle inflated monitor.
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register tagged_monitor = mark;
const Register zero = tmp2;
// Try to CAS m->owner from null to current thread.
// If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ.
// Otherwise, register zero is filled with the current owner.
z_lghi(zero, 0);
z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), tagged_monitor);
z_bre(locked);
// Check if recursive.
z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction
z_brne(slow_path);
// Recursive
z_agsi(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll);
z_cgr(zero, zero);
// z_bru(locked);
// Uncomment above line in the future, for now jump address is right next to us.
}
BLOCK_COMMENT("} handle_inflated_monitor_lightweight_locking");
bind(locked);
#ifdef ASSERT
// Check that locked label is reached with flag == EQ.
NearLabel flag_correct;
z_bre(flag_correct);
stop("CC is not set to EQ, it should be - lock");
#endif // ASSERT
bind(slow_path);
#ifdef ASSERT
// Check that slow_path label is reached with flag == NE.
z_brne(flag_correct);
stop("CC is not set to NE, it should be - lock");
bind(flag_correct);
#endif // ASSERT
// C2 uses the value of flag (NE vs EQ) to determine the continuation.
}
void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Register tmp1, Register tmp2) {
assert_different_registers(obj, tmp1, tmp2);
// Handle inflated monitor.
NearLabel inflated, inflated_load_monitor;
// Finish fast unlock successfully. MUST reach to with flag == EQ.
NearLabel unlocked;
// Finish fast unlock unsuccessfully. MUST branch to with flag == NE.
NearLabel slow_path;
const Register mark = tmp1;
const Register top = tmp2;
const int mark_offset = oopDesc::mark_offset_in_bytes();
const ByteSize ls_top_offset = JavaThread::lock_stack_top_offset();
BLOCK_COMMENT("compiler_fast_lightweight_unlock {");
{ // Lightweight Unlock
// Check if obj is top of lock-stack.
z_lgf(top, Address(Z_thread, ls_top_offset));
z_aghi(top, -oopSize);
z_cg(obj, Address(Z_thread, top));
branch_optimized(bcondNotEqual, inflated_load_monitor);
// Pop lock-stack.
#ifdef ASSERT
const Register temp_top = tmp1; // let's not kill top here, we can use for recursive check
z_agrk(temp_top, top, Z_thread);
z_xc(0, oopSize-1, temp_top, 0, temp_top); // wipe out lock-stack entry
#endif
z_alsi(in_bytes(JavaThread::lock_stack_top_offset()), Z_thread, -oopSize); // pop object
z_cr(tmp, tmp); // set CC to EQ
z_alsi(in_bytes(ls_top_offset), Z_thread, -oopSize); // pop object
// The underflow check is elided. The recursive check will always fail
// when the lock stack is empty because of the _bad_oop_sentinel field.
// Check if recursive.
z_aghi(top, -oopSize);
z_cg(obj, Address(Z_thread, top));
z_bre(unlocked);
// Not recursive
// Check for monitor (0b10).
z_lg(mark, Address(obj, mark_offset));
z_tmll(mark, markWord::monitor_value);
z_brnaz(inflated);
#ifdef ASSERT
// Check header not unlocked (0b01).
NearLabel not_unlocked;
z_tmll(mark, markWord::unlocked_value);
z_braz(not_unlocked);
stop("lightweight_unlock already unlocked");
bind(not_unlocked);
#endif // ASSERT
{ // Try to unlock. Transition lock bits 0b00 => 0b01
Register unlocked_obj = top;
z_lgr(unlocked_obj, mark);
z_oill(unlocked_obj, markWord::unlocked_value);
z_csg(mark, unlocked_obj, mark_offset, obj);
branch_optimized(Assembler::bcondEqual, unlocked);
}
// Restore lock-stack and handle the unlock in runtime.
z_lgf(top, Address(Z_thread, ls_top_offset));
DEBUG_ONLY(z_stg(obj, Address(Z_thread, top));)
z_alsi(in_bytes(ls_top_offset), Z_thread, oopSize);
// set CC to NE
z_ltgr(obj, obj); // object is not null here
z_bru(slow_path);
}
BLOCK_COMMENT("} compiler_fast_lightweight_unlock");
{ // Handle inflated monitor.
bind(inflated_load_monitor);
z_lg(mark, Address(obj, mark_offset));
#ifdef ASSERT
z_tmll(mark, markWord::monitor_value);
z_brnaz(inflated);
stop("Fast Unlock not monitor");
#endif // ASSERT
bind(inflated);
#ifdef ASSERT
NearLabel check_done, loop;
z_lgf(top, Address(Z_thread, ls_top_offset));
bind(loop);
z_aghi(top, -oopSize);
compareU32_and_branch(top, in_bytes(JavaThread::lock_stack_base_offset()),
bcondLow, check_done);
z_cg(obj, Address(Z_thread, top));
z_brne(loop);
stop("Fast Unlock lock on stack");
bind(check_done);
#endif // ASSERT
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
NearLabel not_recursive;
const Register recursions = tmp2;
// Check if recursive.
load_and_test_long(recursions, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
z_bre(not_recursive); // if 0 then jump, it's not recursive locking
// Recursive unlock
z_agsi(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll);
z_cgr(monitor, monitor); // set the CC to EQUAL
z_bru(unlocked);
bind(not_recursive);
NearLabel not_ok;
// Check if the entry lists are empty.
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
z_brne(not_ok);
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
z_brne(not_ok);
z_release();
z_stg(tmp2 /*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
z_bru(unlocked); // CC = EQ here
bind(not_ok);
// The owner may be anonymous, and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
z_stg(Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
z_bru(slow_path); // CC = NE here
}
bind(unlocked);
#ifdef ASSERT
// Check that unlocked label is reached with flag == EQ.
NearLabel flag_correct;
z_bre(flag_correct);
stop("CC is not set to EQ, it should be - unlock");
#endif // ASSERT
bind(slow_path);
#ifdef ASSERT
// Check that slow_path label is reached with flag == NE.
z_brne(flag_correct);
stop("CC is not set to NE, it should be - unlock");
bind(flag_correct);
#endif // ASSERT
// C2 uses the value of flag (NE vs EQ) to determine the continuation.
}
void MacroAssembler::pop_count_int(Register r_dst, Register r_src, Register r_tmp) {

@ -727,8 +727,10 @@ class MacroAssembler: public Assembler {
void compiler_fast_lock_object(Register oop, Register box, Register temp1, Register temp2);
void compiler_fast_unlock_object(Register oop, Register box, Register temp1, Register temp2);
void lightweight_lock(Register obj, Register hdr, Register tmp, Label& slow);
void lightweight_unlock(Register obj, Register hdr, Register tmp, Label& slow);
void lightweight_lock(Register obj, Register tmp1, Register tmp2, Label& slow);
void lightweight_unlock(Register obj, Register tmp1, Register tmp2, Label& slow);
void compiler_fast_lock_lightweight_object(Register obj, Register tmp1, Register tmp2);
void compiler_fast_unlock_lightweight_object(Register obj, Register tmp1, Register tmp2);
void resolve_jobject(Register value, Register tmp1, Register tmp2);

@ -9579,6 +9579,7 @@ instruct partialSubtypeCheck_vs_zero(flagsReg pcc, rarg2RegP sub, rarg3RegP supe
// inlined locking and unlocking
instruct cmpFastLock(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{
predicate(LockingMode != LM_LIGHTWEIGHT);
match(Set pcc (FastLock oop box));
effect(TEMP tmp1, TEMP tmp2);
ins_cost(100);
@ -9589,6 +9590,7 @@ instruct cmpFastLock(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRe
%}
instruct cmpFastUnlock(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{
predicate(LockingMode != LM_LIGHTWEIGHT);
match(Set pcc (FastUnlock oop box));
effect(TEMP tmp1, TEMP tmp2);
ins_cost(100);
@ -9598,6 +9600,38 @@ instruct cmpFastUnlock(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, i
ins_pipe(pipe_class_dummy);
%}
instruct cmpFastLockLightweight(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{
predicate(LockingMode == LM_LIGHTWEIGHT);
match(Set pcc (FastLock oop box));
effect(TEMP tmp1, TEMP tmp2);
ins_cost(100);
// TODO: s390 port size(VARIABLE_SIZE);
format %{ "FASTLOCK $oop, $box; KILL Z_ARG4, Z_ARG5" %}
ins_encode %{
__ fast_lock_lightweight($oop$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register);
// If locking was successful, cc should indicate 'EQ'.
// The compiler generates a branch to the runtime call to
// _complete_monitor_locking_Java for the case where cc is 'NE'.
%}
ins_pipe(pipe_class_dummy);
%}
instruct cmpFastUnlockLightweight(flagsReg pcc, iRegP_N2P oop, iRegP_N2P box, iRegP tmp1, iRegP tmp2) %{
predicate(LockingMode == LM_LIGHTWEIGHT);
match(Set pcc (FastUnlock oop box));
effect(TEMP tmp1, TEMP tmp2);
ins_cost(100);
// TODO: s390 port size(FIXED_SIZE);
format %{ "FASTUNLOCK $oop, $box; KILL Z_ARG4, Z_ARG5" %}
ins_encode %{
__ fast_unlock_lightweight($oop$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register);
// If unlocking was successful, cc should indicate 'EQ'.
// The compiler generates a branch to the runtime call to
// _complete_monitor_unlocking_Java for the case where cc is 'NE'.
%}
ins_pipe(pipe_class_dummy);
%}
instruct inlineCallClearArrayConst(SSlenDW cnt, iRegP_N2P base, Universe dummy, flagsReg cr) %{
match(Set dummy (ClearArray cnt base));
effect(KILL cr);

@ -1711,8 +1711,13 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
__ add2reg(r_box, lock_offset, Z_SP);
// Try fastpath for locking.
// Fast_lock kills r_temp_1, r_temp_2.
__ compiler_fast_lock_object(r_oop, r_box, r_tmp1, r_tmp2);
if (LockingMode == LM_LIGHTWEIGHT) {
// Fast_lock kills r_temp_1, r_temp_2.
__ compiler_fast_lock_lightweight_object(r_oop, r_tmp1, r_tmp2);
} else {
// Fast_lock kills r_temp_1, r_temp_2.
__ compiler_fast_lock_object(r_oop, r_box, r_tmp1, r_tmp2);
}
__ z_bre(done);
//-------------------------------------------------------------------------
@ -1910,8 +1915,13 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm,
__ add2reg(r_box, lock_offset, Z_SP);
// Try fastpath for unlocking.
// Fast_unlock kills r_tmp1, r_tmp2.
__ compiler_fast_unlock_object(r_oop, r_box, r_tmp1, r_tmp2);
if (LockingMode == LM_LIGHTWEIGHT) {
// Fast_unlock kills r_tmp1, r_tmp2.
__ compiler_fast_unlock_lightweight_object(r_oop, r_tmp1, r_tmp2);
} else {
// Fast_unlock kills r_tmp1, r_tmp2.
__ compiler_fast_unlock_object(r_oop, r_box, r_tmp1, r_tmp2);
}
__ z_bre(done);
// Slow path for unlocking.

@ -413,6 +413,8 @@ class VM_Version: public Abstract_VM_Version {
// s390 supports fast class initialization checks
static bool supports_fast_class_init_checks() { return true; }
constexpr static bool supports_recursive_lightweight_locking() { return true; }
// CPU feature query functions
static const char* get_model_string() { return _model_string; }
static bool has_StoreFacilityListExtended() { return (_features[0] & StoreFacilityListExtendedMask) == StoreFacilityListExtendedMask; }