8319801: Recursive lightweight locking: aarch64 implementation

Co-authored-by: Stefan Karlsson <stefank@openjdk.org>
Co-authored-by: Erik Österlund <eosterlund@openjdk.org>
Reviewed-by: rkennke, coleenp, dcubed, aph
This commit is contained in:
Axel Boldt-Christmas 2024-02-16 07:18:31 +00:00
parent 9029bf644e
commit 18cea823a1
9 changed files with 410 additions and 136 deletions

View File

@ -16433,13 +16433,12 @@ instruct branchLoopEnd(cmpOp cmp, rFlagsReg cr, label lbl)
instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2, iRegPNoSp tmp3)
%{
predicate(LockingMode != LM_LIGHTWEIGHT);
match(Set cr (FastLock object box));
effect(TEMP tmp, TEMP tmp2, TEMP tmp3);
// TODO
// identify correct cost
ins_cost(5 * INSN_COST);
format %{ "fastlock $object,$box\t! kills $tmp,$tmp2" %}
format %{ "fastlock $object,$box\t! kills $tmp,$tmp2,$tmp3" %}
ins_encode %{
__ fast_lock($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
@ -16450,6 +16449,7 @@ instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegP
instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2)
%{
predicate(LockingMode != LM_LIGHTWEIGHT);
match(Set cr (FastUnlock object box));
effect(TEMP tmp, TEMP tmp2);
@ -16463,6 +16463,37 @@ instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRe
ins_pipe(pipe_serial);
%}
instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2)
%{
predicate(LockingMode == LM_LIGHTWEIGHT);
match(Set cr (FastLock object box));
effect(TEMP tmp, TEMP tmp2);
ins_cost(5 * INSN_COST);
format %{ "fastlock $object,$box\t! kills $tmp,$tmp2" %}
ins_encode %{
__ fast_lock_lightweight($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register);
%}
ins_pipe(pipe_serial);
%}
instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp, iRegPNoSp tmp2)
%{
predicate(LockingMode == LM_LIGHTWEIGHT);
match(Set cr (FastUnlock object box));
effect(TEMP tmp, TEMP tmp2);
ins_cost(5 * INSN_COST);
format %{ "fastunlock $object,$box\t! kills $tmp, $tmp2" %}
ins_encode %{
__ fast_unlock_lightweight($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register);
%}
ins_pipe(pipe_serial);
%}
// ============================================================================
// Safepoint Instructions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -80,12 +80,12 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
br(Assembler::NE, slow_case);
}
// Load object header
ldr(hdr, Address(obj, hdr_offset));
if (LockingMode == LM_LIGHTWEIGHT) {
lightweight_lock(obj, hdr, temp, rscratch2, slow_case);
} else if (LockingMode == LM_LEGACY) {
Label done;
// Load object header
ldr(hdr, Address(obj, hdr_offset));
// and mark it as unlocked
orr(hdr, hdr, markWord::unlocked_value);
// save unlocked object header into the displaced header location on the stack
@ -144,11 +144,6 @@ void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_
verify_oop(obj);
if (LockingMode == LM_LIGHTWEIGHT) {
ldr(hdr, Address(obj, oopDesc::mark_offset_in_bytes()));
// We cannot use tbnz here, the target might be too far away and cannot
// be encoded.
tst(hdr, markWord::monitor_value);
br(Assembler::NE, slow_case);
lightweight_unlock(obj, hdr, temp, rscratch2, slow_case);
} else if (LockingMode == LM_LEGACY) {
// test if object header is pointing to the displaced header, and if so, restore

View File

@ -32,6 +32,7 @@
#include "opto/output.hpp"
#include "opto/subnode.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/globalDefinitions.hpp"
#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
@ -55,6 +56,7 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register
Label object_has_monitor;
Label count, no_count;
assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight");
assert_different_registers(oop, box, tmp, disp_hdr);
// Load markWord from object into displaced_header.
@ -73,7 +75,8 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register
if (LockingMode == LM_MONITOR) {
tst(oop, oop); // Set NE to indicate 'failure' -> take slow-path. We know that oop != 0.
b(cont);
} else if (LockingMode == LM_LEGACY) {
} else {
assert(LockingMode == LM_LEGACY, "must be");
// Set tmp to be (markWord of object | UNLOCK_VALUE).
orr(tmp, disp_hdr, markWord::unlocked_value);
@ -102,10 +105,6 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register
ands(tmp/*==0?*/, disp_hdr, tmp); // Sets flags for result
str(tmp/*==0, perhaps*/, Address(box, BasicLock::displaced_header_offset_in_bytes()));
b(cont);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
lightweight_lock(oop, disp_hdr, tmp, tmp3Reg, no_count);
b(count);
}
// Handle existing monitor.
@ -119,14 +118,13 @@ void C2_MacroAssembler::fast_lock(Register objectReg, Register boxReg, Register
cmpxchg(tmp, zr, rthread, Assembler::xword, /*acquire*/ true,
/*release*/ true, /*weak*/ false, tmp3Reg); // Sets flags for result
if (LockingMode != LM_LIGHTWEIGHT) {
// Store a non-null value into the box to avoid looking like a re-entrant
// lock. The fast-path monitor unlock code checks for
// markWord::monitor_value so use markWord::unused_mark which has the
// relevant bit set, and also matches ObjectSynchronizer::enter.
mov(tmp, (address)markWord::unused_mark().value());
str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes()));
}
// Store a non-null value into the box to avoid looking like a re-entrant
// lock. The fast-path monitor unlock code checks for
// markWord::monitor_value so use markWord::unused_mark which has the
// relevant bit set, and also matches ObjectSynchronizer::enter.
mov(tmp, (address)markWord::unused_mark().value());
str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes()));
br(Assembler::EQ, cont); // CAS success means locking succeeded
cmp(tmp3Reg, rthread);
@ -157,6 +155,7 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
Label object_has_monitor;
Label count, no_count;
assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight");
assert_different_registers(oop, box, tmp, disp_hdr);
if (LockingMode == LM_LEGACY) {
@ -175,7 +174,8 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
if (LockingMode == LM_MONITOR) {
tst(oop, oop); // Set NE to indicate 'failure' -> take slow-path. We know that oop != 0.
b(cont);
} else if (LockingMode == LM_LEGACY) {
} else {
assert(LockingMode == LM_LEGACY, "must be");
// Check if it is still a light weight lock, this is is true if we
// see the stack address of the basicLock in the markWord of the
// object.
@ -183,10 +183,6 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
cmpxchg(oop, box, disp_hdr, Assembler::xword, /*acquire*/ false,
/*release*/ true, /*weak*/ false, tmp);
b(cont);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
lightweight_unlock(oop, tmp, box, disp_hdr, no_count);
b(count);
}
assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0");
@ -196,19 +192,6 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
STATIC_ASSERT(markWord::monitor_value <= INT_MAX);
add(tmp, tmp, -(int)markWord::monitor_value); // monitor
if (LockingMode == LM_LIGHTWEIGHT) {
// If the owner is anonymous, we need to fix it -- in an outline stub.
Register tmp2 = disp_hdr;
ldr(tmp2, Address(tmp, ObjectMonitor::owner_offset()));
// We cannot use tbnz here, the target might be too far away and cannot
// be encoded.
tst(tmp2, (uint64_t)ObjectMonitor::ANONYMOUS_OWNER);
C2HandleAnonOMOwnerStub* stub = new (Compile::current()->comp_arena()) C2HandleAnonOMOwnerStub(tmp, tmp2);
Compile::current()->output()->add_stub(stub);
br(Assembler::NE, stub->entry());
bind(stub->continuation());
}
ldr(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset()));
Label notRecursive;
@ -241,6 +224,262 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
bind(no_count);
}
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
Register t2, Register t3) {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
assert_different_registers(obj, t1, t2, t3);
// Handle inflated monitor.
Label inflated;
// Finish fast lock successfully. MUST branch to with flag == EQ
Label locked;
// Finish fast lock unsuccessfully. MUST branch to with flag == NE
Label slow_path;
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(t1, obj);
ldrw(t1, Address(t1, Klass::access_flags_offset()));
tstw(t1, JVM_ACC_IS_VALUE_BASED_CLASS);
br(Assembler::NE, slow_path);
}
const Register t1_mark = t1;
{ // Lightweight locking
// Push lock to the lock stack and finish successfully. MUST branch to with flag == EQ
Label push;
const Register t2_top = t2;
const Register t3_t = t3;
// Check if lock-stack is full.
ldrw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
cmpw(t2_top, (unsigned)LockStack::end_offset() - 1);
br(Assembler::GT, slow_path);
// Check if recursive.
subw(t3_t, t2_top, oopSize);
ldr(t3_t, Address(rthread, t3_t));
cmp(obj, t3_t);
br(Assembler::EQ, push);
// Relaxed normal load to check for monitor. Optimization for monitor case.
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
// Not inflated
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid a lea");
// Try to lock. Transition lock-bits 0b01 => 0b00
orr(t1_mark, t1_mark, markWord::unlocked_value);
eor(t3_t, t1_mark, markWord::unlocked_value);
cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword,
/*acquire*/ true, /*release*/ false, /*weak*/ false, noreg);
br(Assembler::NE, slow_path);
bind(push);
// After successful lock, push object on lock-stack.
str(obj, Address(rthread, t2_top));
addw(t2_top, t2_top, oopSize);
strw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
b(locked);
}
{ // Handle inflated monitor.
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register t1_tagged_monitor = t1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register t2_owner_addr = t2;
const Register t3_owner = t3;
// Compute owner address.
lea(t2_owner_addr, Address(t1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
// CAS owner (null => current thread).
cmpxchg(t2_owner_addr, zr, rthread, Assembler::xword, /*acquire*/ true,
/*release*/ false, /*weak*/ false, t3_owner);
br(Assembler::EQ, locked);
// Check if recursive.
cmp(t3_owner, rthread);
br(Assembler::NE, slow_path);
// Recursive.
increment(Address(t1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1);
}
bind(locked);
increment(Address(rthread, JavaThread::held_monitor_count_offset()));
#ifdef ASSERT
// Check that locked label is reached with Flags == EQ.
Label flag_correct;
br(Assembler::EQ, flag_correct);
stop("Fast Lock Flag != EQ");
#endif
bind(slow_path);
#ifdef ASSERT
// Check that slow_path label is reached with Flags == NE.
br(Assembler::NE, flag_correct);
stop("Fast Lock Flag != NE");
bind(flag_correct);
#endif
// C2 uses the value of Flags (NE vs EQ) to determine the continuation.
}
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Register t2,
Register t3) {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
assert_different_registers(obj, t1, t2, t3);
// Handle inflated monitor.
Label inflated, inflated_load_monitor;
// Finish fast unlock successfully. MUST branch to with flag == EQ
Label unlocked;
// Finish fast unlock unsuccessfully. MUST branch to with flag == NE
Label slow_path;
const Register t1_mark = t1;
const Register t2_top = t2;
const Register t3_t = t3;
{ // Lightweight unlock
// Check if obj is top of lock-stack.
ldrw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
subw(t2_top, t2_top, oopSize);
ldr(t3_t, Address(rthread, t2_top));
cmp(obj, t3_t);
// Top of lock stack was not obj. Must be monitor.
br(Assembler::NE, inflated_load_monitor);
// Pop lock-stack.
DEBUG_ONLY(str(zr, Address(rthread, t2_top));)
strw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
// Check if recursive.
subw(t3_t, t2_top, oopSize);
ldr(t3_t, Address(rthread, t3_t));
cmp(obj, t3_t);
br(Assembler::EQ, unlocked);
// Not recursive.
// Load Mark.
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
// Check header for monitor (0b10).
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
// Try to unlock. Transition lock bits 0b00 => 0b01
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
orr(t3_t, t1_mark, markWord::unlocked_value);
cmpxchg(/*addr*/ obj, /*expected*/ t1_mark, /*new*/ t3_t, Assembler::xword,
/*acquire*/ false, /*release*/ true, /*weak*/ false, noreg);
br(Assembler::EQ, unlocked);
// Compare and exchange failed.
// Restore lock-stack and handle the unlock in runtime.
DEBUG_ONLY(str(obj, Address(rthread, t2_top));)
addw(t2_top, t2_top, oopSize);
str(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
b(slow_path);
}
{ // Handle inflated monitor.
bind(inflated_load_monitor);
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
#ifdef ASSERT
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
stop("Fast Unlock not monitor");
#endif
bind(inflated);
#ifdef ASSERT
Label check_done;
subw(t2_top, t2_top, oopSize);
cmpw(t2_top, in_bytes(JavaThread::lock_stack_base_offset()));
br(Assembler::LT, check_done);
ldr(t3_t, Address(rthread, t2_top));
cmp(obj, t3_t);
br(Assembler::NE, inflated);
stop("Fast Unlock lock on stack");
bind(check_done);
#endif
// mark contains the tagged ObjectMonitor*.
const Register t1_monitor = t1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
// Untag the monitor.
sub(t1_monitor, t1_mark, monitor_tag);
const Register t2_recursions = t2;
Label not_recursive;
// Check if recursive.
ldr(t2_recursions, Address(t1_monitor, ObjectMonitor::recursions_offset()));
cbz(t2_recursions, not_recursive);
// Recursive unlock.
sub(t2_recursions, t2_recursions, 1u);
str(t2_recursions, Address(t1_monitor, ObjectMonitor::recursions_offset()));
// Set flag == EQ
cmp(t2_recursions, t2_recursions);
b(unlocked);
bind(not_recursive);
Label release;
const Register t2_owner_addr = t2;
// Compute owner address.
lea(t2_owner_addr, Address(t1_monitor, ObjectMonitor::owner_offset()));
// Check if the entry lists are empty.
ldr(rscratch1, Address(t1_monitor, ObjectMonitor::EntryList_offset()));
ldr(t3_t, Address(t1_monitor, ObjectMonitor::cxq_offset()));
orr(rscratch1, rscratch1, t3_t);
cmp(rscratch1, zr);
br(Assembler::EQ, release);
// 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.
str(rthread, Address(t2_owner_addr));
b(slow_path);
bind(release);
// Set owner to null.
// Release to satisfy the JMM
stlr(zr, t2_owner_addr);
}
bind(unlocked);
decrement(Address(rthread, JavaThread::held_monitor_count_offset()));
#ifdef ASSERT
// Check that unlocked label is reached with Flags == EQ.
Label flag_correct;
br(Assembler::EQ, flag_correct);
stop("Fast Unlock Flag != EQ");
#endif
bind(slow_path);
#ifdef ASSERT
// Check that slow_path label is reached with Flags == NE.
br(Assembler::NE, flag_correct);
stop("Fast Unlock Flag != NE");
bind(flag_correct);
#endif
// C2 uses the value of Flags (NE vs EQ) to determine the continuation.
}
// Search for str1 in str2 and return index or -1
// Clobbers: rscratch1, rscratch2, rflags. May also clobber v0-v1, when icnt1==-1.
void C2_MacroAssembler::string_indexof(Register str2, Register str1,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -36,9 +36,11 @@
public:
// Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file.
// See full description in macroAssembler_aarch64.cpp.
void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3);
void fast_unlock(Register object, Register box, Register tmp, Register tmp2);
// Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file.
void fast_lock_lightweight(Register object, Register t1, Register t2, Register t3);
void fast_unlock_lightweight(Register object, Register t1, Register t2, Register t3);
void string_compare(Register str1, Register str2,
Register cnt1, Register cnt2, Register result,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -701,7 +701,6 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
}
if (LockingMode == LM_LIGHTWEIGHT) {
ldr(tmp, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
lightweight_lock(obj_reg, tmp, tmp2, tmp3, slow_case);
b(count);
} else if (LockingMode == LM_LEGACY) {
@ -818,22 +817,6 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg)
if (LockingMode == LM_LIGHTWEIGHT) {
Label slow_case;
// Check for non-symmetric locking. This is allowed by the spec and the interpreter
// must handle it.
Register tmp = rscratch1;
// First check for lock-stack underflow.
ldrw(tmp, Address(rthread, JavaThread::lock_stack_top_offset()));
cmpw(tmp, (unsigned)LockStack::start_offset());
br(Assembler::LE, slow_case);
// Then check if the top of the lock-stack matches the unlocked object.
subw(tmp, tmp, oopSize);
ldr(tmp, Address(rthread, tmp));
cmpoop(tmp, obj_reg);
br(Assembler::NE, slow_case);
ldr(header_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
tbnz(header_reg, exact_log2(markWord::monitor_value), slow_case);
lightweight_unlock(obj_reg, header_reg, swap_reg, tmp_reg, slow_case);
b(count);
bind(slow_case);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -23,8 +23,6 @@
*
*/
#include <sys/types.h>
#include "precompiled.hpp"
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
@ -56,6 +54,7 @@
#include "runtime/jniHandles.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/powerOfTwo.hpp"
#ifdef COMPILER1
#include "c1/c1_LIRAssembler.hpp"
@ -67,6 +66,8 @@
#include "opto/output.hpp"
#endif
#include <sys/types.h>
#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
#else
@ -6381,97 +6382,122 @@ void MacroAssembler::double_move(VMRegPair src, VMRegPair dst, Register tmp) {
}
// Implements lightweight-locking.
// Branches to slow upon failure to lock the object, with ZF cleared.
// Falls through upon success with ZF set.
//
// - obj: the object to be locked
// - hdr: the header, already loaded from obj, will be destroyed
// - t1, t2: temporary registers, will be destroyed
void MacroAssembler::lightweight_lock(Register obj, Register hdr, Register t1, Register t2, Label& slow) {
// - t1, t2, t3: temporary registers, will be destroyed
// - slow: branched to if locking fails, absolute offset may larger than 32KB (imm14 encoding).
void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, Register t3, Label& slow) {
assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking");
assert_different_registers(obj, hdr, t1, t2, rscratch1);
assert_different_registers(obj, t1, t2, t3, rscratch1);
// Check if we would have space on lock-stack for the object.
ldrw(t1, Address(rthread, JavaThread::lock_stack_top_offset()));
cmpw(t1, (unsigned)LockStack::end_offset() - 1);
br(Assembler::GT, slow);
Label push;
const Register top = t1;
const Register mark = t2;
const Register t = t3;
// Load (object->mark() | 1) into hdr
orr(hdr, hdr, markWord::unlocked_value);
// Clear lock-bits, into t2
eor(t2, hdr, markWord::unlocked_value);
// Try to swing header from unlocked to locked
// Clobbers rscratch1 when UseLSE is false
cmpxchg(/*addr*/ obj, /*expected*/ hdr, /*new*/ t2, Assembler::xword,
/*acquire*/ true, /*release*/ true, /*weak*/ false, t1);
// Preload the markWord. It is important that this is the first
// instruction emitted as it is part of C1's null check semantics.
ldr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
// Check if the lock-stack is full.
ldrw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
cmpw(top, (unsigned)LockStack::end_offset());
br(Assembler::GE, slow);
// Check for recursion.
subw(t, top, oopSize);
ldr(t, Address(rthread, t));
cmp(obj, t);
br(Assembler::EQ, push);
// Check header for monitor (0b10).
tst(mark, markWord::monitor_value);
br(Assembler::NE, slow);
// After successful lock, push object on lock-stack
ldrw(t1, Address(rthread, JavaThread::lock_stack_top_offset()));
str(obj, Address(rthread, t1));
addw(t1, t1, oopSize);
strw(t1, Address(rthread, JavaThread::lock_stack_top_offset()));
// Try to lock. Transition lock bits 0b01 => 0b00
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
orr(mark, mark, markWord::unlocked_value);
eor(t, mark, markWord::unlocked_value);
cmpxchg(/*addr*/ obj, /*expected*/ mark, /*new*/ t, Assembler::xword,
/*acquire*/ true, /*release*/ false, /*weak*/ false, noreg);
br(Assembler::NE, slow);
bind(push);
// After successful lock, push object on lock-stack.
str(obj, Address(rthread, top));
addw(top, top, oopSize);
strw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
}
// Implements lightweight-unlocking.
// Branches to slow upon failure, with ZF cleared.
// Falls through upon success, with ZF set.
//
// - obj: the object to be unlocked
// - hdr: the (pre-loaded) header of the object
// - t1, t2: temporary registers
void MacroAssembler::lightweight_unlock(Register obj, Register hdr, Register t1, Register t2, Label& slow) {
// - t1, t2, t3: temporary registers
// - slow: branched to if unlocking fails, absolute offset may larger than 32KB (imm14 encoding).
void MacroAssembler::lightweight_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow) {
assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking");
assert_different_registers(obj, hdr, t1, t2, rscratch1);
// cmpxchg clobbers rscratch1.
assert_different_registers(obj, t1, t2, t3, rscratch1);
#ifdef ASSERT
{
// 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;
ldrw(t1, Address(rthread, JavaThread::lock_stack_top_offset()));
cmpw(t1, (unsigned)LockStack::start_offset());
br(Assembler::GT, stack_ok);
br(Assembler::GE, stack_ok);
STOP("Lock-stack underflow");
bind(stack_ok);
}
{
// Check if the top of the lock-stack matches the unlocked object.
Label tos_ok;
subw(t1, t1, oopSize);
ldr(t1, Address(rthread, t1));
cmpoop(t1, obj);
br(Assembler::EQ, tos_ok);
STOP("Top of lock-stack does not match the unlocked object");
bind(tos_ok);
}
{
// Check that hdr is fast-locked.
Label hdr_ok;
tst(hdr, markWord::lock_mask_in_place);
br(Assembler::EQ, hdr_ok);
STOP("Header is not fast-locked");
bind(hdr_ok);
}
#endif
// Load the new header (unlocked) into t1
orr(t1, hdr, markWord::unlocked_value);
Label unlocked, push_and_slow;
const Register top = t1;
const Register mark = t2;
const Register t = t3;
// Try to swing header from locked to unlocked
// Clobbers rscratch1 when UseLSE is false
cmpxchg(obj, hdr, t1, Assembler::xword,
/*acquire*/ true, /*release*/ true, /*weak*/ false, t2);
// Check if obj is top of lock-stack.
ldrw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
subw(top, top, oopSize);
ldr(t, Address(rthread, top));
cmp(obj, t);
br(Assembler::NE, slow);
// After successful unlock, pop object from lock-stack
ldrw(t1, Address(rthread, JavaThread::lock_stack_top_offset()));
subw(t1, t1, oopSize);
// Pop lock-stack.
DEBUG_ONLY(str(zr, Address(rthread, top));)
strw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
// Check if recursive.
subw(t, top, oopSize);
ldr(t, Address(rthread, t));
cmp(obj, t);
br(Assembler::EQ, unlocked);
// Not recursive. Check header for monitor (0b10).
ldr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
tbnz(mark, log2i_exact(markWord::monitor_value), push_and_slow);
#ifdef ASSERT
str(zr, Address(rthread, t1));
// Check header not unlocked (0b01).
Label not_unlocked;
tbz(mark, log2i_exact(markWord::unlocked_value), not_unlocked);
stop("lightweight_unlock already unlocked");
bind(not_unlocked);
#endif
strw(t1, Address(rthread, JavaThread::lock_stack_top_offset()));
// Try to unlock. Transition lock bits 0b00 => 0b01
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
orr(t, mark, markWord::unlocked_value);
cmpxchg(obj, mark, t, Assembler::xword,
/*acquire*/ false, /*release*/ true, /*weak*/ false, noreg);
br(Assembler::EQ, unlocked);
bind(push_and_slow);
// Restore lock-stack and handle the unlock in runtime.
DEBUG_ONLY(str(obj, Address(rthread, top));)
addw(top, top, oopSize);
strw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
b(slow);
bind(unlocked);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -1602,8 +1602,8 @@ public:
// Code for java.lang.Thread::onSpinWait() intrinsic.
void spin_wait();
void lightweight_lock(Register obj, Register hdr, Register t1, Register t2, Label& slow);
void lightweight_unlock(Register obj, Register hdr, Register t1, Register t2, Label& slow);
void lightweight_lock(Register obj, Register t1, Register t2, Register t3, Label& slow);
void lightweight_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow);
private:
// Check the current thread doesn't need a cross modify fence.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved.
* Copyright (c) 2021, Azul Systems, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@ -1796,7 +1796,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
__ br(Assembler::NE, slow_path_lock);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
__ ldr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
__ lightweight_lock(obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock);
}
__ bind(count);
@ -1939,8 +1938,6 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
__ decrement(Address(rthread, JavaThread::held_monitor_count_offset()));
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "");
__ ldr(old_hdr, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
__ tbnz(old_hdr, exact_log2(markWord::monitor_value), slow_path_unlock);
__ lightweight_unlock(obj_reg, old_hdr, swap_reg, lock_tmp, slow_path_unlock);
__ decrement(Address(rthread, JavaThread::held_monitor_count_offset()));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -169,6 +169,7 @@ enum Ampere_CPU_Model {
// Aarch64 supports fast class initialization checks
static bool supports_fast_class_init_checks() { return true; }
constexpr static bool supports_stack_watermark_barrier() { return true; }
constexpr static bool supports_recursive_lightweight_locking() { return true; }
static void get_compatible_board(char *buf, int buflen);