mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-15 13:49:42 +02:00
8364141: Remove LockingMode related code from x86
Reviewed-by: aboldtch, dholmes, coleenp
This commit is contained in:
parent
b81f4faed7
commit
f155f7d6e5
10 changed files with 91 additions and 600 deletions
|
@ -413,11 +413,7 @@ int LIR_Assembler::emit_unwind_handler() {
|
|||
if (method()->is_synchronized()) {
|
||||
monitor_address(0, FrameMap::rax_opr);
|
||||
stub = new MonitorExitStub(FrameMap::rax_opr, true, 0);
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
__ jmp(*stub->entry());
|
||||
} else {
|
||||
__ unlock_object(rdi, rsi, rax, *stub->entry());
|
||||
}
|
||||
__ unlock_object(rdi, rsi, rax, *stub->entry());
|
||||
__ bind(*stub->continuation());
|
||||
}
|
||||
|
||||
|
@ -2733,15 +2729,9 @@ void LIR_Assembler::emit_lock(LIR_OpLock* op) {
|
|||
Register obj = op->obj_opr()->as_register(); // may not be an oop
|
||||
Register hdr = op->hdr_opr()->as_register();
|
||||
Register lock = op->lock_opr()->as_register();
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
if (op->info() != nullptr) {
|
||||
add_debug_info_for_null_check_here(op->info());
|
||||
__ null_check(obj);
|
||||
}
|
||||
__ jmp(*op->stub()->entry());
|
||||
} else if (op->code() == lir_lock) {
|
||||
if (op->code() == lir_lock) {
|
||||
assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header");
|
||||
Register tmp = LockingMode == LM_LIGHTWEIGHT ? op->scratch_opr()->as_register() : noreg;
|
||||
Register tmp = op->scratch_opr()->as_register();
|
||||
// add debug info for NullPointerException only if one is possible
|
||||
int null_check_offset = __ lock_object(hdr, obj, lock, tmp, *op->stub()->entry());
|
||||
if (op->info() != nullptr) {
|
||||
|
|
|
@ -289,7 +289,7 @@ void LIRGenerator::do_MonitorEnter(MonitorEnter* x) {
|
|||
// this CodeEmitInfo must not have the xhandlers because here the
|
||||
// object is already locked (xhandlers expect object to be unlocked)
|
||||
CodeEmitInfo* info = state_for(x, x->state(), true);
|
||||
LIR_Opr tmp = LockingMode == LM_LIGHTWEIGHT ? new_register(T_ADDRESS) : LIR_OprFact::illegalOpr;
|
||||
LIR_Opr tmp = new_register(T_ADDRESS);
|
||||
monitor_enter(obj.result(), lock, syncTempOpr(), tmp,
|
||||
x->monitor_no(), info_for_exception, info);
|
||||
}
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Register tmp, Label& slow_case) {
|
||||
const int aligned_mask = BytesPerWord -1;
|
||||
const int hdr_offset = oopDesc::mark_offset_in_bytes();
|
||||
assert(hdr == rax, "hdr must be rax, for the cmpxchg instruction");
|
||||
assert_different_registers(hdr, obj, disp_hdr, tmp);
|
||||
int null_check_offset = -1;
|
||||
|
@ -55,93 +53,20 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
|
|||
|
||||
null_check_offset = offset();
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
lightweight_lock(disp_hdr, obj, hdr, tmp, slow_case);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
Label done;
|
||||
|
||||
if (DiagnoseSyncOnValueBasedClasses != 0) {
|
||||
load_klass(hdr, obj, rscratch1);
|
||||
testb(Address(hdr, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class);
|
||||
jcc(Assembler::notZero, slow_case);
|
||||
}
|
||||
|
||||
// Load object header
|
||||
movptr(hdr, Address(obj, hdr_offset));
|
||||
// and mark it as unlocked
|
||||
orptr(hdr, markWord::unlocked_value);
|
||||
// save unlocked object header into the displaced header location on the stack
|
||||
movptr(Address(disp_hdr, 0), hdr);
|
||||
// test if object header is still the same (i.e. unlocked), and if so, store the
|
||||
// displaced header address in the object header - if it is not the same, get the
|
||||
// object header instead
|
||||
MacroAssembler::lock(); // must be immediately before cmpxchg!
|
||||
cmpxchgptr(disp_hdr, Address(obj, hdr_offset));
|
||||
// if the object header was the same, we're done
|
||||
jcc(Assembler::equal, done);
|
||||
// if the object header was not the same, it is now in the hdr register
|
||||
// => test if it is a stack pointer into the same stack (recursive locking), i.e.:
|
||||
//
|
||||
// 1) (hdr & aligned_mask) == 0
|
||||
// 2) rsp <= hdr
|
||||
// 3) hdr <= rsp + page_size
|
||||
//
|
||||
// these 3 tests can be done by evaluating the following expression:
|
||||
//
|
||||
// (hdr - rsp) & (aligned_mask - page_size)
|
||||
//
|
||||
// assuming both the stack pointer and page_size have their least
|
||||
// significant 2 bits cleared and page_size is a power of 2
|
||||
subptr(hdr, rsp);
|
||||
andptr(hdr, aligned_mask - (int)os::vm_page_size());
|
||||
// for recursive locking, the result is zero => save it in the displaced header
|
||||
// location (null in the displaced hdr location indicates recursive locking)
|
||||
movptr(Address(disp_hdr, 0), hdr);
|
||||
// otherwise we don't care about the result and handle locking via runtime call
|
||||
jcc(Assembler::notZero, slow_case);
|
||||
// done
|
||||
bind(done);
|
||||
inc_held_monitor_count();
|
||||
}
|
||||
lightweight_lock(disp_hdr, obj, hdr, tmp, slow_case);
|
||||
|
||||
return null_check_offset;
|
||||
}
|
||||
|
||||
void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) {
|
||||
const int aligned_mask = BytesPerWord -1;
|
||||
const int hdr_offset = oopDesc::mark_offset_in_bytes();
|
||||
assert(disp_hdr == rax, "disp_hdr must be rax, for the cmpxchg instruction");
|
||||
assert(hdr != obj && hdr != disp_hdr && obj != disp_hdr, "registers must be different");
|
||||
Label done;
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT) {
|
||||
// load displaced header
|
||||
movptr(hdr, Address(disp_hdr, 0));
|
||||
// if the loaded hdr is null we had recursive locking
|
||||
testptr(hdr, hdr);
|
||||
// if we had recursive locking, we are done
|
||||
jcc(Assembler::zero, done);
|
||||
}
|
||||
|
||||
// load object
|
||||
movptr(obj, Address(disp_hdr, BasicObjectLock::obj_offset()));
|
||||
verify_oop(obj);
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
lightweight_unlock(obj, disp_hdr, hdr, 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
|
||||
// the displaced header, get the object header instead
|
||||
MacroAssembler::lock(); // must be immediately before cmpxchg!
|
||||
cmpxchgptr(hdr, Address(obj, hdr_offset));
|
||||
// if the object header was not pointing to the displaced header,
|
||||
// we do unlocking via runtime call
|
||||
jcc(Assembler::notEqual, slow_case);
|
||||
// done
|
||||
bind(done);
|
||||
dec_held_monitor_count();
|
||||
}
|
||||
lightweight_unlock(obj, disp_hdr, hdr, slow_case);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -219,244 +219,11 @@ inline Assembler::AvxVectorLen C2_MacroAssembler::vector_length_encoding(int vle
|
|||
|
||||
|
||||
// obj: object to lock
|
||||
// box: on-stack box address (displaced header location) - KILLED
|
||||
// rax,: tmp -- KILLED
|
||||
// scr: tmp -- KILLED
|
||||
void C2_MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg,
|
||||
Register scrReg, Register cx1Reg, Register cx2Reg, Register thread,
|
||||
Metadata* method_data) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_lock_lightweight");
|
||||
// Ensure the register assignments are disjoint
|
||||
assert(tmpReg == rax, "");
|
||||
assert(cx1Reg == noreg, "");
|
||||
assert(cx2Reg == noreg, "");
|
||||
assert_different_registers(objReg, boxReg, tmpReg, scrReg);
|
||||
|
||||
// Possible cases that we'll encounter in fast_lock
|
||||
// ------------------------------------------------
|
||||
// * Inflated
|
||||
// -- unlocked
|
||||
// -- Locked
|
||||
// = by self
|
||||
// = by other
|
||||
// * neutral
|
||||
// * stack-locked
|
||||
// -- by self
|
||||
// = sp-proximity test hits
|
||||
// = sp-proximity test generates false-negative
|
||||
// -- by other
|
||||
//
|
||||
|
||||
Label IsInflated, DONE_LABEL, NO_COUNT, COUNT;
|
||||
|
||||
if (DiagnoseSyncOnValueBasedClasses != 0) {
|
||||
load_klass(tmpReg, objReg, scrReg);
|
||||
testb(Address(tmpReg, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class);
|
||||
jcc(Assembler::notZero, DONE_LABEL);
|
||||
}
|
||||
|
||||
movptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // [FETCH]
|
||||
testptr(tmpReg, markWord::monitor_value); // inflated vs stack-locked|neutral
|
||||
jcc(Assembler::notZero, IsInflated);
|
||||
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
// Clear ZF so that we take the slow path at the DONE label. objReg is known to be not 0.
|
||||
testptr(objReg, objReg);
|
||||
} else {
|
||||
assert(LockingMode == LM_LEGACY, "must be");
|
||||
// Attempt stack-locking ...
|
||||
orptr (tmpReg, markWord::unlocked_value);
|
||||
movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS
|
||||
lock();
|
||||
cmpxchgptr(boxReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Updates tmpReg
|
||||
jcc(Assembler::equal, COUNT); // Success
|
||||
|
||||
// Recursive locking.
|
||||
// The object is stack-locked: markword contains stack pointer to BasicLock.
|
||||
// Locked by current thread if difference with current SP is less than one page.
|
||||
subptr(tmpReg, rsp);
|
||||
// Next instruction set ZFlag == 1 (Success) if difference is less then one page.
|
||||
andptr(tmpReg, (int32_t) (7 - (int)os::vm_page_size()) );
|
||||
movptr(Address(boxReg, 0), tmpReg);
|
||||
}
|
||||
jmp(DONE_LABEL);
|
||||
|
||||
bind(IsInflated);
|
||||
// The object is inflated. tmpReg contains pointer to ObjectMonitor* + markWord::monitor_value
|
||||
|
||||
// Unconditionally set box->_displaced_header = markWord::unused_mark().
|
||||
// Without cast to int32_t this style of movptr will destroy r10 which is typically obj.
|
||||
movptr(Address(boxReg, 0), checked_cast<int32_t>(markWord::unused_mark().value()));
|
||||
|
||||
// It's inflated and we use scrReg for ObjectMonitor* in this section.
|
||||
movptr(boxReg, Address(r15_thread, JavaThread::monitor_owner_id_offset()));
|
||||
movq(scrReg, tmpReg);
|
||||
xorq(tmpReg, tmpReg);
|
||||
lock();
|
||||
cmpxchgptr(boxReg, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
|
||||
|
||||
// Propagate ICC.ZF from CAS above into DONE_LABEL.
|
||||
jccb(Assembler::equal, COUNT); // CAS above succeeded; propagate ZF = 1 (success)
|
||||
|
||||
cmpptr(boxReg, rax); // Check if we are already the owner (recursive lock)
|
||||
jccb(Assembler::notEqual, NO_COUNT); // If not recursive, ZF = 0 at this point (fail)
|
||||
incq(Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
xorq(rax, rax); // Set ZF = 1 (success) for recursive lock, denoting locking success
|
||||
bind(DONE_LABEL);
|
||||
|
||||
// ZFlag == 1 count in fast path
|
||||
// ZFlag == 0 count in slow path
|
||||
jccb(Assembler::notZero, NO_COUNT); // jump if ZFlag == 0
|
||||
|
||||
bind(COUNT);
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
// Count monitors in fast path
|
||||
increment(Address(thread, JavaThread::held_monitor_count_offset()));
|
||||
}
|
||||
xorl(tmpReg, tmpReg); // Set ZF == 1
|
||||
|
||||
bind(NO_COUNT);
|
||||
|
||||
// At NO_COUNT the icc ZFlag is set as follows ...
|
||||
// fast_unlock uses the same protocol.
|
||||
// ZFlag == 1 -> Success
|
||||
// ZFlag == 0 -> Failure - force control through the slow path
|
||||
}
|
||||
|
||||
// obj: object to unlock
|
||||
// box: box address (displaced header location), killed. Must be EAX.
|
||||
// tmp: killed, cannot be obj nor box.
|
||||
//
|
||||
// Some commentary on balanced locking:
|
||||
//
|
||||
// fast_lock and fast_unlock are emitted only for provably balanced lock sites.
|
||||
// Methods that don't have provably balanced locking are forced to run in the
|
||||
// interpreter - such methods won't be compiled to use fast_lock and fast_unlock.
|
||||
// The interpreter provides two properties:
|
||||
// I1: At return-time the interpreter automatically and quietly unlocks any
|
||||
// objects acquired the current activation (frame). Recall that the
|
||||
// interpreter maintains an on-stack list of locks currently held by
|
||||
// a frame.
|
||||
// I2: If a method attempts to unlock an object that is not held by the
|
||||
// the frame the interpreter throws IMSX.
|
||||
//
|
||||
// Lets say A(), which has provably balanced locking, acquires O and then calls B().
|
||||
// B() doesn't have provably balanced locking so it runs in the interpreter.
|
||||
// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O
|
||||
// is still locked by A().
|
||||
//
|
||||
// The only other source of unbalanced locking would be JNI. The "Java Native Interface:
|
||||
// Programmer's Guide and Specification" claims that an object locked by jni_monitorenter
|
||||
// should not be unlocked by "normal" java-level locking and vice-versa. The specification
|
||||
// doesn't specify what will occur if a program engages in such mixed-mode locking, however.
|
||||
// Arguably given that the spec legislates the JNI case as undefined our implementation
|
||||
// could reasonably *avoid* checking owner in fast_unlock().
|
||||
// In the interest of performance we elide m->Owner==Self check in unlock.
|
||||
// A perfectly viable alternative is to elide the owner check except when
|
||||
// Xcheck:jni is enabled.
|
||||
|
||||
void C2_MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpReg) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "lightweight locking should use fast_unlock_lightweight");
|
||||
assert(boxReg == rax, "");
|
||||
assert_different_registers(objReg, boxReg, tmpReg);
|
||||
|
||||
Label DONE_LABEL, Stacked, COUNT, NO_COUNT;
|
||||
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
cmpptr(Address(boxReg, 0), NULL_WORD); // Examine the displaced header
|
||||
jcc (Assembler::zero, COUNT); // 0 indicates recursive stack-lock
|
||||
}
|
||||
movptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Examine the object's markword
|
||||
if (LockingMode != LM_MONITOR) {
|
||||
testptr(tmpReg, markWord::monitor_value); // Inflated?
|
||||
jcc(Assembler::zero, Stacked);
|
||||
}
|
||||
|
||||
// It's inflated.
|
||||
|
||||
// Despite our balanced locking property we still check that m->_owner == Self
|
||||
// as java routines or native JNI code called by this thread might
|
||||
// have released the lock.
|
||||
//
|
||||
// If there's no contention try a 1-0 exit. That is, exit without
|
||||
// a costly MEMBAR or CAS. See synchronizer.cpp for details on how
|
||||
// we detect and recover from the race that the 1-0 exit admits.
|
||||
//
|
||||
// Conceptually fast_unlock() must execute a STST|LDST "release" barrier
|
||||
// before it STs null into _owner, releasing the lock. Updates
|
||||
// to data protected by the critical section must be visible before
|
||||
// we drop the lock (and thus before any other thread could acquire
|
||||
// the lock and observe the fields protected by the lock).
|
||||
// IA32's memory-model is SPO, so STs are ordered with respect to
|
||||
// each other and there's no need for an explicit barrier (fence).
|
||||
// See also http://gee.cs.oswego.edu/dl/jmm/cookbook.html.
|
||||
Label LSuccess, LNotRecursive;
|
||||
|
||||
cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0);
|
||||
jccb(Assembler::equal, LNotRecursive);
|
||||
|
||||
// Recursive inflated unlock
|
||||
decrement(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
jmpb(LSuccess);
|
||||
|
||||
bind(LNotRecursive);
|
||||
|
||||
// Set owner to null.
|
||||
// Release to satisfy the JMM
|
||||
movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
|
||||
// We need a full fence after clearing owner to avoid stranding.
|
||||
// StoreLoad achieves this.
|
||||
membar(StoreLoad);
|
||||
|
||||
// Check if the entry_list is empty.
|
||||
cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(entry_list)), NULL_WORD);
|
||||
jccb(Assembler::zero, LSuccess); // If so we are done.
|
||||
|
||||
// Check if there is a successor.
|
||||
cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD);
|
||||
jccb(Assembler::notZero, LSuccess); // If so we are done.
|
||||
|
||||
// Save the monitor pointer in the current thread, so we can try to
|
||||
// reacquire the lock in SharedRuntime::monitor_exit_helper().
|
||||
andptr(tmpReg, ~(int32_t)markWord::monitor_value);
|
||||
movptr(Address(r15_thread, JavaThread::unlocked_inflated_monitor_offset()), tmpReg);
|
||||
|
||||
orl (boxReg, 1); // set ICC.ZF=0 to indicate failure
|
||||
jmpb (DONE_LABEL);
|
||||
|
||||
bind (LSuccess);
|
||||
testl (boxReg, 0); // set ICC.ZF=1 to indicate success
|
||||
jmpb (DONE_LABEL);
|
||||
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
bind (Stacked);
|
||||
movptr(tmpReg, Address (boxReg, 0)); // re-fetch
|
||||
lock();
|
||||
cmpxchgptr(tmpReg, Address(objReg, oopDesc::mark_offset_in_bytes())); // Uses RAX which is box
|
||||
// Intentional fall-thru into DONE_LABEL
|
||||
}
|
||||
|
||||
bind(DONE_LABEL);
|
||||
|
||||
// ZFlag == 1 count in fast path
|
||||
// ZFlag == 0 count in slow path
|
||||
jccb(Assembler::notZero, NO_COUNT);
|
||||
|
||||
bind(COUNT);
|
||||
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
// Count monitors in fast path
|
||||
decrementq(Address(r15_thread, JavaThread::held_monitor_count_offset()));
|
||||
}
|
||||
|
||||
xorl(tmpReg, tmpReg); // Set ZF == 1
|
||||
|
||||
bind(NO_COUNT);
|
||||
}
|
||||
|
||||
// box: on-stack box address -- KILLED
|
||||
// rax: tmp -- KILLED
|
||||
// t : tmp -- KILLED
|
||||
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register rax_reg,
|
||||
Register t, Register thread) {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
assert(rax_reg == rax, "Used for CAS");
|
||||
assert_different_registers(obj, box, rax_reg, t, thread);
|
||||
|
||||
|
@ -616,8 +383,39 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
|
|||
// C2 uses the value of ZF to determine the continuation.
|
||||
}
|
||||
|
||||
// obj: object to lock
|
||||
// rax: tmp -- KILLED
|
||||
// t : tmp - cannot be obj nor rax -- KILLED
|
||||
//
|
||||
// Some commentary on balanced locking:
|
||||
//
|
||||
// fast_lock and fast_unlock are emitted only for provably balanced lock sites.
|
||||
// Methods that don't have provably balanced locking are forced to run in the
|
||||
// interpreter - such methods won't be compiled to use fast_lock and fast_unlock.
|
||||
// The interpreter provides two properties:
|
||||
// I1: At return-time the interpreter automatically and quietly unlocks any
|
||||
// objects acquired in the current activation (frame). Recall that the
|
||||
// interpreter maintains an on-stack list of locks currently held by
|
||||
// a frame.
|
||||
// I2: If a method attempts to unlock an object that is not held by the
|
||||
// frame the interpreter throws IMSX.
|
||||
//
|
||||
// Lets say A(), which has provably balanced locking, acquires O and then calls B().
|
||||
// B() doesn't have provably balanced locking so it runs in the interpreter.
|
||||
// Control returns to A() and A() unlocks O. By I1 and I2, above, we know that O
|
||||
// is still locked by A().
|
||||
//
|
||||
// The only other source of unbalanced locking would be JNI. The "Java Native Interface
|
||||
// Specification" states that an object locked by JNI's MonitorEnter should not be
|
||||
// unlocked by "normal" java-level locking and vice-versa. The specification doesn't
|
||||
// specify what will occur if a program engages in such mixed-mode locking, however.
|
||||
// Arguably given that the spec legislates the JNI case as undefined our implementation
|
||||
// could reasonably *avoid* checking owner in fast_unlock().
|
||||
// In the interest of performance we elide m->Owner==Self check in unlock.
|
||||
// A perfectly viable alternative is to elide the owner check except when
|
||||
// Xcheck:jni is enabled.
|
||||
|
||||
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread) {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
assert(reg_rax == rax, "Used for CAS");
|
||||
assert_different_registers(obj, reg_rax, t);
|
||||
|
||||
|
|
|
@ -34,12 +34,7 @@ public:
|
|||
Assembler::AvxVectorLen vector_length_encoding(int vlen_in_bytes);
|
||||
|
||||
// Code used by cmpFastLock and cmpFastUnlock mach instructions in .ad file.
|
||||
// See full description in macroAssembler_x86.cpp.
|
||||
void fast_lock(Register obj, Register box, Register tmp,
|
||||
Register scr, Register cx1, Register cx2, Register thread,
|
||||
Metadata* method_data);
|
||||
void fast_unlock(Register obj, Register box, Register tmp);
|
||||
|
||||
// See full description in c2_MacroAssembler_x86.cpp.
|
||||
void fast_lock_lightweight(Register obj, Register box, Register rax_reg,
|
||||
Register t, Register thread);
|
||||
void fast_unlock_lightweight(Register obj, Register reg_rax, Register t, Register thread);
|
||||
|
|
|
@ -1024,100 +1024,25 @@ void InterpreterMacroAssembler::get_method_counters(Register method,
|
|||
void InterpreterMacroAssembler::lock_object(Register lock_reg) {
|
||||
assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1");
|
||||
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
call_VM_preemptable(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
} else {
|
||||
Label count_locking, done, slow_case;
|
||||
Label done, slow_case;
|
||||
|
||||
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
|
||||
const Register tmp_reg = rbx;
|
||||
const Register obj_reg = c_rarg3; // Will contain the oop
|
||||
const Register rklass_decode_tmp = rscratch1;
|
||||
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
|
||||
const Register tmp_reg = rbx;
|
||||
const Register obj_reg = c_rarg3; // Will contain the oop
|
||||
|
||||
const int obj_offset = in_bytes(BasicObjectLock::obj_offset());
|
||||
const int lock_offset = in_bytes(BasicObjectLock::lock_offset());
|
||||
const int mark_offset = lock_offset +
|
||||
BasicLock::displaced_header_offset_in_bytes();
|
||||
// Load object pointer into obj_reg
|
||||
movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset()));
|
||||
|
||||
// Load object pointer into obj_reg
|
||||
movptr(obj_reg, Address(lock_reg, obj_offset));
|
||||
lightweight_lock(lock_reg, obj_reg, swap_reg, tmp_reg, slow_case);
|
||||
jmp(done);
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
lightweight_lock(lock_reg, obj_reg, swap_reg, tmp_reg, slow_case);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
if (DiagnoseSyncOnValueBasedClasses != 0) {
|
||||
load_klass(tmp_reg, obj_reg, rklass_decode_tmp);
|
||||
testb(Address(tmp_reg, Klass::misc_flags_offset()), KlassFlags::_misc_is_value_based_class);
|
||||
jcc(Assembler::notZero, slow_case);
|
||||
}
|
||||
bind(slow_case);
|
||||
|
||||
// Load immediate 1 into swap_reg %rax
|
||||
movl(swap_reg, 1);
|
||||
|
||||
// Load (object->mark() | 1) into swap_reg %rax
|
||||
orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
// Save (object->mark() | 1) into BasicLock's displaced header
|
||||
movptr(Address(lock_reg, mark_offset), swap_reg);
|
||||
|
||||
assert(lock_offset == 0,
|
||||
"displaced header must be first word in BasicObjectLock");
|
||||
|
||||
lock();
|
||||
cmpxchgptr(lock_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
jcc(Assembler::zero, count_locking);
|
||||
|
||||
const int zero_bits = 7;
|
||||
|
||||
// Fast check for recursive lock.
|
||||
//
|
||||
// Can apply the optimization only if this is a stack lock
|
||||
// allocated in this thread. For efficiency, we can focus on
|
||||
// recently allocated stack locks (instead of reading the stack
|
||||
// base and checking whether 'mark' points inside the current
|
||||
// thread stack):
|
||||
// 1) (mark & zero_bits) == 0, and
|
||||
// 2) rsp <= mark < mark + os::pagesize()
|
||||
//
|
||||
// Warning: rsp + os::pagesize can overflow the stack base. We must
|
||||
// neither apply the optimization for an inflated lock allocated
|
||||
// just above the thread stack (this is why condition 1 matters)
|
||||
// nor apply the optimization if the stack lock is inside the stack
|
||||
// of another thread. The latter is avoided even in case of overflow
|
||||
// because we have guard pages at the end of all stacks. Hence, if
|
||||
// we go over the stack base and hit the stack of another thread,
|
||||
// this should not be in a writeable area that could contain a
|
||||
// stack lock allocated by that thread. As a consequence, a stack
|
||||
// lock less than page size away from rsp is guaranteed to be
|
||||
// owned by the current thread.
|
||||
//
|
||||
// These 3 tests can be done by evaluating the following
|
||||
// expression: ((mark - rsp) & (zero_bits - os::vm_page_size())),
|
||||
// assuming both stack pointer and pagesize have their
|
||||
// least significant bits clear.
|
||||
// NOTE: the mark is in swap_reg %rax as the result of cmpxchg
|
||||
subptr(swap_reg, rsp);
|
||||
andptr(swap_reg, zero_bits - (int)os::vm_page_size());
|
||||
|
||||
// Save the test result, for recursive case, the result is zero
|
||||
movptr(Address(lock_reg, mark_offset), swap_reg);
|
||||
jcc(Assembler::notZero, slow_case);
|
||||
|
||||
bind(count_locking);
|
||||
inc_held_monitor_count();
|
||||
}
|
||||
jmp(done);
|
||||
|
||||
bind(slow_case);
|
||||
|
||||
// Call the runtime routine for slow case
|
||||
call_VM_preemptable(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
bind(done);
|
||||
}
|
||||
// Call the runtime routine for slow case
|
||||
call_VM_preemptable(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
bind(done);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1136,63 +1061,31 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
|
|||
void InterpreterMacroAssembler::unlock_object(Register lock_reg) {
|
||||
assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1");
|
||||
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg);
|
||||
} else {
|
||||
Label count_locking, done, slow_case;
|
||||
Label done, slow_case;
|
||||
|
||||
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
|
||||
const Register header_reg = c_rarg2; // Will contain the old oopMark
|
||||
const Register obj_reg = c_rarg3; // Will contain the oop
|
||||
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
|
||||
const Register header_reg = c_rarg2; // Will contain the old oopMark
|
||||
const Register obj_reg = c_rarg3; // Will contain the oop
|
||||
|
||||
save_bcp(); // Save in case of exception
|
||||
save_bcp(); // Save in case of exception
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT) {
|
||||
// Convert from BasicObjectLock structure to object and BasicLock
|
||||
// structure Store the BasicLock address into %rax
|
||||
lea(swap_reg, Address(lock_reg, BasicObjectLock::lock_offset()));
|
||||
}
|
||||
// Load oop into obj_reg(%c_rarg3)
|
||||
movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset()));
|
||||
|
||||
// Load oop into obj_reg(%c_rarg3)
|
||||
movptr(obj_reg, Address(lock_reg, BasicObjectLock::obj_offset()));
|
||||
// Free entry
|
||||
movptr(Address(lock_reg, BasicObjectLock::obj_offset()), NULL_WORD);
|
||||
|
||||
// Free entry
|
||||
movptr(Address(lock_reg, BasicObjectLock::obj_offset()), NULL_WORD);
|
||||
lightweight_unlock(obj_reg, swap_reg, header_reg, slow_case);
|
||||
jmp(done);
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
lightweight_unlock(obj_reg, swap_reg, header_reg, slow_case);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// Load the old header from BasicLock structure
|
||||
movptr(header_reg, Address(swap_reg,
|
||||
BasicLock::displaced_header_offset_in_bytes()));
|
||||
bind(slow_case);
|
||||
// Call the runtime routine for slow case.
|
||||
movptr(Address(lock_reg, BasicObjectLock::obj_offset()), obj_reg); // restore obj
|
||||
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg);
|
||||
|
||||
// Test for recursion
|
||||
testptr(header_reg, header_reg);
|
||||
bind(done);
|
||||
|
||||
// zero for recursive case
|
||||
jcc(Assembler::zero, count_locking);
|
||||
|
||||
// Atomic swap back the old header
|
||||
lock();
|
||||
cmpxchgptr(header_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
// zero for simple unlock of a stack-lock case
|
||||
jcc(Assembler::notZero, slow_case);
|
||||
|
||||
bind(count_locking);
|
||||
dec_held_monitor_count();
|
||||
}
|
||||
jmp(done);
|
||||
|
||||
bind(slow_case);
|
||||
// Call the runtime routine for slow case.
|
||||
movptr(Address(lock_reg, BasicObjectLock::obj_offset()), obj_reg); // restore obj
|
||||
call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), lock_reg);
|
||||
|
||||
bind(done);
|
||||
|
||||
restore_bcp();
|
||||
}
|
||||
restore_bcp();
|
||||
}
|
||||
|
||||
void InterpreterMacroAssembler::test_method_data_pointer(Register mdp,
|
||||
|
|
|
@ -59,17 +59,10 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas
|
|||
|
||||
__ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
if (!UseObjectMonitorTable) {
|
||||
// check if monitor
|
||||
__ testptr(result, markWord::monitor_value);
|
||||
__ jcc(Assembler::notZero, slowCase);
|
||||
}
|
||||
} else {
|
||||
// check if locked
|
||||
__ testptr(result, markWord::unlocked_value);
|
||||
__ jcc(Assembler::zero, slowCase);
|
||||
if (!UseObjectMonitorTable) {
|
||||
// check if monitor
|
||||
__ testptr(result, markWord::monitor_value);
|
||||
__ jcc(Assembler::notZero, slowCase);
|
||||
}
|
||||
|
||||
// get hash
|
||||
|
|
|
@ -2133,7 +2133,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
// We use the same pc/oopMap repeatedly when we call out
|
||||
|
||||
Label native_return;
|
||||
if (LockingMode != LM_LEGACY && method->is_object_wait0()) {
|
||||
if (method->is_object_wait0()) {
|
||||
// For convenience we use the pc we want to resume to in case of preemption on Object.wait.
|
||||
__ set_last_Java_frame(rsp, noreg, native_return, rscratch1);
|
||||
} else {
|
||||
|
@ -2174,16 +2174,11 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
const Register swap_reg = rax; // Must use rax for cmpxchg instruction
|
||||
const Register obj_reg = rbx; // Will contain the oop
|
||||
const Register lock_reg = r13; // Address of compiler lock object (BasicLock)
|
||||
const Register old_hdr = r13; // value of old header at unlock time
|
||||
|
||||
Label slow_path_lock;
|
||||
Label lock_done;
|
||||
|
||||
if (method->is_synchronized()) {
|
||||
Label count_mon;
|
||||
|
||||
const int mark_word_offset = BasicLock::displaced_header_offset_in_bytes();
|
||||
|
||||
// Get the handle (the 2nd argument)
|
||||
__ mov(oop_handle_reg, c_rarg1);
|
||||
|
||||
|
@ -2194,47 +2189,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
// Load the oop from the handle
|
||||
__ movptr(obj_reg, Address(oop_handle_reg, 0));
|
||||
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
__ jmp(slow_path_lock);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// Load immediate 1 into swap_reg %rax
|
||||
__ movl(swap_reg, 1);
|
||||
|
||||
// Load (object->mark() | 1) into swap_reg %rax
|
||||
__ orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
// Save (object->mark() | 1) into BasicLock's displaced header
|
||||
__ movptr(Address(lock_reg, mark_word_offset), swap_reg);
|
||||
|
||||
// src -> dest iff dest == rax else rax <- dest
|
||||
__ lock();
|
||||
__ cmpxchgptr(lock_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
__ jcc(Assembler::equal, count_mon);
|
||||
|
||||
// Hmm should this move to the slow path code area???
|
||||
|
||||
// Test if the oopMark is an obvious stack pointer, i.e.,
|
||||
// 1) (mark & 3) == 0, and
|
||||
// 2) rsp <= mark < mark + os::pagesize()
|
||||
// These 3 tests can be done by evaluating the following
|
||||
// expression: ((mark - rsp) & (3 - os::vm_page_size())),
|
||||
// assuming both stack pointer and pagesize have their
|
||||
// least significant 2 bits clear.
|
||||
// NOTE: the oopMark is in swap_reg %rax as the result of cmpxchg
|
||||
|
||||
__ subptr(swap_reg, rsp);
|
||||
__ andptr(swap_reg, 3 - (int)os::vm_page_size());
|
||||
|
||||
// Save the test result, for recursive case, the result is zero
|
||||
__ movptr(Address(lock_reg, mark_word_offset), swap_reg);
|
||||
__ jcc(Assembler::notEqual, slow_path_lock);
|
||||
|
||||
__ bind(count_mon);
|
||||
__ inc_held_monitor_count();
|
||||
} else {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
__ lightweight_lock(lock_reg, obj_reg, swap_reg, rscratch1, slow_path_lock);
|
||||
}
|
||||
__ lightweight_lock(lock_reg, obj_reg, swap_reg, rscratch1, slow_path_lock);
|
||||
|
||||
// Slow path will re-enter here
|
||||
__ bind(lock_done);
|
||||
|
@ -2322,7 +2277,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
// change thread state
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java);
|
||||
|
||||
if (LockingMode != LM_LEGACY && method->is_object_wait0()) {
|
||||
if (method->is_object_wait0()) {
|
||||
// Check preemption for Object.wait()
|
||||
__ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset()));
|
||||
__ cmpptr(rscratch1, NULL_WORD);
|
||||
|
@ -2354,38 +2309,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
|||
// Get locked oop from the handle we passed to jni
|
||||
__ movptr(obj_reg, Address(oop_handle_reg, 0));
|
||||
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
Label not_recur;
|
||||
// Simple recursive lock?
|
||||
__ cmpptr(Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size), NULL_WORD);
|
||||
__ jcc(Assembler::notEqual, not_recur);
|
||||
__ dec_held_monitor_count();
|
||||
__ jmpb(fast_done);
|
||||
__ bind(not_recur);
|
||||
}
|
||||
|
||||
// Must save rax if it is live now because cmpxchg must use it
|
||||
if (ret_type != T_FLOAT && ret_type != T_DOUBLE && ret_type != T_VOID) {
|
||||
save_native_result(masm, ret_type, stack_slots);
|
||||
}
|
||||
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
__ jmp(slow_path_unlock);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// get address of the stack lock
|
||||
__ lea(rax, Address(rsp, lock_slot_offset * VMRegImpl::stack_slot_size));
|
||||
// get old displaced header
|
||||
__ movptr(old_hdr, Address(rax, 0));
|
||||
|
||||
// Atomic swap old header if oop still contains the stack lock
|
||||
__ lock();
|
||||
__ cmpxchgptr(old_hdr, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
|
||||
__ jcc(Assembler::notEqual, slow_path_unlock);
|
||||
__ dec_held_monitor_count();
|
||||
} else {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
__ lightweight_unlock(obj_reg, swap_reg, lock_reg, slow_path_unlock);
|
||||
}
|
||||
__ lightweight_unlock(obj_reg, swap_reg, lock_reg, slow_path_unlock);
|
||||
|
||||
// slow path re-enters here
|
||||
__ bind(unlock_done);
|
||||
|
|
|
@ -1017,21 +1017,16 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
|
|||
// change thread state
|
||||
__ movl(Address(thread, JavaThread::thread_state_offset()), _thread_in_Java);
|
||||
|
||||
if (LockingMode != LM_LEGACY) {
|
||||
// Check preemption for Object.wait()
|
||||
Label not_preempted;
|
||||
__ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset()));
|
||||
__ cmpptr(rscratch1, NULL_WORD);
|
||||
__ jccb(Assembler::equal, not_preempted);
|
||||
__ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD);
|
||||
__ jmp(rscratch1);
|
||||
__ bind(native_return);
|
||||
__ restore_after_resume(true /* is_native */);
|
||||
__ bind(not_preempted);
|
||||
} else {
|
||||
// any pc will do so just use this one for LM_LEGACY to keep code together.
|
||||
__ bind(native_return);
|
||||
}
|
||||
// Check preemption for Object.wait()
|
||||
Label not_preempted;
|
||||
__ movptr(rscratch1, Address(r15_thread, JavaThread::preempt_alternate_return_offset()));
|
||||
__ cmpptr(rscratch1, NULL_WORD);
|
||||
__ jccb(Assembler::equal, not_preempted);
|
||||
__ movptr(Address(r15_thread, JavaThread::preempt_alternate_return_offset()), NULL_WORD);
|
||||
__ jmp(rscratch1);
|
||||
__ bind(native_return);
|
||||
__ restore_after_resume(true /* is_native */);
|
||||
__ bind(not_preempted);
|
||||
|
||||
// reset_last_Java_frame
|
||||
__ reset_last_Java_frame(true);
|
||||
|
|
|
@ -14073,33 +14073,7 @@ instruct jmpConUCF2_short(cmpOpUCF2 cop, rFlagsRegUCF cmp, label labl) %{
|
|||
// ============================================================================
|
||||
// inlined locking and unlocking
|
||||
|
||||
instruct cmpFastLock(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI tmp, rRegP scr) %{
|
||||
predicate(LockingMode != LM_LIGHTWEIGHT);
|
||||
match(Set cr (FastLock object box));
|
||||
effect(TEMP tmp, TEMP scr, USE_KILL box);
|
||||
ins_cost(300);
|
||||
format %{ "fastlock $object,$box\t! kills $box,$tmp,$scr" %}
|
||||
ins_encode %{
|
||||
__ fast_lock($object$$Register, $box$$Register, $tmp$$Register,
|
||||
$scr$$Register, noreg, noreg, r15_thread, nullptr);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpFastUnlock(rFlagsReg cr, rRegP object, rax_RegP box, rRegP tmp) %{
|
||||
predicate(LockingMode != LM_LIGHTWEIGHT);
|
||||
match(Set cr (FastUnlock object box));
|
||||
effect(TEMP tmp, USE_KILL box);
|
||||
ins_cost(300);
|
||||
format %{ "fastunlock $object,$box\t! kills $box,$tmp" %}
|
||||
ins_encode %{
|
||||
__ fast_unlock($object$$Register, $box$$Register, $tmp$$Register);
|
||||
%}
|
||||
ins_pipe(pipe_slow);
|
||||
%}
|
||||
|
||||
instruct cmpFastLockLightweight(rFlagsReg cr, rRegP object, rbx_RegP box, rax_RegI rax_reg, rRegP tmp) %{
|
||||
predicate(LockingMode == LM_LIGHTWEIGHT);
|
||||
match(Set cr (FastLock object box));
|
||||
effect(TEMP rax_reg, TEMP tmp, USE_KILL box);
|
||||
ins_cost(300);
|
||||
|
@ -14111,7 +14085,6 @@ instruct cmpFastLockLightweight(rFlagsReg cr, rRegP object, rbx_RegP box, rax_Re
|
|||
%}
|
||||
|
||||
instruct cmpFastUnlockLightweight(rFlagsReg cr, rRegP object, rax_RegP rax_reg, rRegP tmp) %{
|
||||
predicate(LockingMode == LM_LIGHTWEIGHT);
|
||||
match(Set cr (FastUnlock object rax_reg));
|
||||
effect(TEMP tmp, USE_KILL rax_reg);
|
||||
ins_cost(300);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue