Skip to content

Commit 57e40d5

Browse files
committed
Allow virtual threads to unmount when blocked on synchronized
1 parent ccdd279 commit 57e40d5

File tree

102 files changed

+2166
-350
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2166
-350
lines changed

src/hotspot/cpu/aarch64/c1_Runtime1_aarch64.cpp

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -170,44 +170,65 @@ class StubFrame: public StackObj {
170170
private:
171171
StubAssembler* _sasm;
172172
bool _return_state;
173+
bool _use_pop_on_epilogue;
173174

174-
public:
175-
StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, return_state_t return_state=requires_return);
176-
void load_argument(int offset_in_words, Register reg);
175+
StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments,
176+
return_state_t return_state, bool use_pop_on_epilogue);
177177

178+
public:
179+
StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, bool use_pop_on_epilogue);
180+
StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, return_state_t return_state);
181+
StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments);
178182
~StubFrame();
183+
184+
void load_argument(int offset_in_words, Register reg);
179185
};;
180186

181187
void StubAssembler::prologue(const char* name, bool must_gc_arguments) {
182188
set_info(name, must_gc_arguments);
183189
enter();
184190
}
185191

186-
void StubAssembler::epilogue() {
187-
leave();
192+
void StubAssembler::epilogue(bool use_pop) {
193+
// Avoid using a leave instruction when this frame may
194+
// have been frozen, since the current value of rfp
195+
// restored from the stub would be invalid. We still
196+
// must restore the rfp value saved on enter though.
197+
if (use_pop) {
198+
ldp(rfp, lr, Address(post(sp, 2 * wordSize)));
199+
} else {
200+
leave();
201+
}
188202
ret(lr);
189203
}
190204

191205
#define __ _sasm->
192206

193-
StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments, return_state_t return_state) {
194-
_sasm = sasm;
195-
_return_state = return_state;
207+
StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments,
208+
return_state_t return_state, bool use_pop_on_epilogue)
209+
: _sasm(sasm), _return_state(return_state), _use_pop_on_epilogue(use_pop_on_epilogue) {
196210
__ prologue(name, must_gc_arguments);
197211
}
198212

213+
StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments,
214+
bool use_pop_on_epilogue) :
215+
StubFrame(sasm, name, must_gc_arguments, requires_return, use_pop_on_epilogue) {}
216+
217+
StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments,
218+
return_state_t return_state) :
219+
StubFrame(sasm, name, must_gc_arguments, return_state, /*use_pop_on_epilogue*/false) {}
220+
221+
StubFrame::StubFrame(StubAssembler* sasm, const char* name, bool must_gc_arguments) :
222+
StubFrame(sasm, name, must_gc_arguments, requires_return, /*use_pop_on_epilogue*/false) {}
223+
199224
// load parameters that were stored with LIR_Assembler::store_parameter
200225
// Note: offsets for store_parameter and load_argument must match
201226
void StubFrame::load_argument(int offset_in_words, Register reg) {
202227
__ load_parameter(offset_in_words, reg);
203228
}
204229

205230
StubFrame::~StubFrame() {
206-
if (_return_state == requires_return) {
207-
__ epilogue();
208-
} else {
209-
__ should_not_reach_here();
210-
}
231+
__ epilogue(_use_pop_on_epilogue);
211232
}
212233

213234
#undef __
@@ -252,7 +273,7 @@ static OopMap* generate_oop_map(StubAssembler* sasm, bool save_fpu_registers) {
252273

253274
for (int i = 0; i < FrameMap::nof_cpu_regs; i++) {
254275
Register r = as_Register(i);
255-
if (i <= 18 && i != rscratch1->encoding() && i != rscratch2->encoding()) {
276+
if (r == rthread || (i <= 18 && i != rscratch1->encoding() && i != rscratch2->encoding())) {
256277
int sp_offset = cpu_reg_save_offsets[i];
257278
oop_map->set_callee_saved(VMRegImpl::stack2reg(sp_offset),
258279
r->as_VMReg());
@@ -337,6 +358,15 @@ void Runtime1::initialize_pd() {
337358
}
338359
}
339360

361+
// return: offset in 64-bit words.
362+
uint Runtime1::runtime_blob_current_thread_offset(frame f) {
363+
CodeBlob* cb = f.cb();
364+
assert(cb == Runtime1::blob_for(C1StubId::monitorenter_id) ||
365+
cb == Runtime1::blob_for(C1StubId::monitorenter_nofpu_id), "must be");
366+
assert(cb != nullptr && cb->is_runtime_stub(), "invalid frame");
367+
int offset = cpu_reg_save_offsets[rthread->encoding()];
368+
return offset / 2; // SP offsets are in halfwords
369+
}
340370

341371
// target: the entry point of the method that creates and posts the exception oop
342372
// has_argument: true if the exception needs arguments (passed in rscratch1 and rscratch2)
@@ -862,7 +892,7 @@ OopMapSet* Runtime1::generate_code_for(C1StubId id, StubAssembler* sasm) {
862892
// fall through
863893
case C1StubId::monitorenter_id:
864894
{
865-
StubFrame f(sasm, "monitorenter", dont_gc_arguments);
895+
StubFrame f(sasm, "monitorenter", dont_gc_arguments, /*use_pop_on_epilogue*/true);
866896
OopMap* map = save_live_registers(sasm, save_fpu_registers);
867897

868898
// Called with store_parameter and not C abi

src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -129,6 +129,12 @@ void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) {
129129
}
130130
}
131131

132+
inline void FreezeBase::prepare_freeze_interpreted_top_frame(const frame& f) {
133+
assert(*f.addr_at(frame::interpreter_frame_last_sp_offset) == 0, "should be null for top frame");
134+
intptr_t* lspp = f.addr_at(frame::interpreter_frame_last_sp_offset);
135+
*lspp = f.unextended_sp() - f.fp();
136+
}
137+
132138
inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) {
133139
assert(hf.fp() == hf.unextended_sp() + (f.fp() - f.unextended_sp()), "");
134140
assert((f.at(frame::interpreter_frame_last_sp_offset) != 0)
@@ -235,7 +241,7 @@ template<typename FKind> frame ThawBase::new_stack_frame(const frame& hf, frame&
235241
int fsize = FKind::size(hf);
236242
intptr_t* frame_sp = caller.unextended_sp() - fsize;
237243
if (bottom || caller.is_interpreted_frame()) {
238-
int argsize = hf.compiled_frame_stack_argsize();
244+
int argsize = FKind::stack_argsize(hf);
239245

240246
fsize += argsize;
241247
frame_sp -= argsize;
@@ -253,7 +259,7 @@ template<typename FKind> frame ThawBase::new_stack_frame(const frame& hf, frame&
253259
fp = frame_sp + FKind::size(hf) - frame::sender_sp_offset;
254260
} else {
255261
fp = FKind::stub
256-
? frame_sp + fsize - frame::sender_sp_offset // on AArch64, this value is used for the safepoint stub
262+
? frame_sp + fsize - frame::sender_sp_offset // fp always points to the address below the pushed return pc. We need correct address.
257263
: *(intptr_t**)(hf.sp() - frame::sender_sp_offset); // we need to re-read fp because it may be an oop and we might have fixed the frame.
258264
}
259265
return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); // TODO PERF : this computes deopt state; is it necessary?
@@ -277,6 +283,44 @@ inline void ThawBase::patch_pd(frame& f, const frame& caller) {
277283
patch_callee_link(caller, caller.fp());
278284
}
279285

286+
inline void ThawBase::patch_pd(frame& f, intptr_t* caller_sp) {
287+
intptr_t* fp = caller_sp - frame::sender_sp_offset;
288+
patch_callee_link(f, fp);
289+
}
290+
291+
inline intptr_t* ThawBase::possibly_adjust_frame(frame& top) {
292+
intptr_t* sp = top.sp();
293+
CodeBlob* cb = top.cb();
294+
295+
if (cb->frame_size() == 2) {
296+
// C2 runtime stub case. For aarch64 the real size of the c2 runtime stub is 2 words bigger
297+
// than what we think, i.e. size is 4. This is because the _last_Java_sp is not set to the
298+
// sp right before making the call to the VM, but rather it is artificially set 2 words above
299+
// this real sp so that we can store the return address at last_Java_sp[-1], and keep this
300+
// property where we can retrieve the last_Java_pc from the last_Java_sp. But that means that
301+
// once we return to the runtime stub, the code will adjust sp according to this real size.
302+
// So we must adjust the frame size back here and we copy lr/rfp again.
303+
sp -= 2;
304+
sp[-2] = sp[0];
305+
sp[-1] = sp[1];
306+
307+
log_develop_trace(continuations, preempt)("adjusted sp for c2 runtime stub, initial sp: " INTPTR_FORMAT " final sp: " INTPTR_FORMAT
308+
" fp: " INTPTR_FORMAT, p2i(sp + frame::metadata_words), p2i(sp), sp[-2]);
309+
}
310+
return sp;
311+
}
312+
313+
inline intptr_t* ThawBase::push_cleanup_continuation() {
314+
frame enterSpecial = new_entry_frame();
315+
intptr_t* sp = enterSpecial.sp();
316+
317+
sp[-1] = (intptr_t)ContinuationEntry::cleanup_pc();
318+
sp[-2] = (intptr_t)enterSpecial.fp();
319+
320+
log_develop_trace(continuations, preempt)("push_cleanup_continuation initial sp: " INTPTR_FORMAT " final sp: " INTPTR_FORMAT, p2i(sp + 2 * frame::metadata_words), p2i(sp));
321+
return sp;
322+
}
323+
280324
inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) {
281325
// Make sure that last_sp is kept relativized.
282326
assert((intptr_t*)f.at_relative(frame::interpreter_frame_last_sp_offset) == f.unextended_sp(), "");

src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,22 @@ static inline intptr_t** link_address(const frame& f) {
4040
: (intptr_t**)(f.unextended_sp() + f.cb()->frame_size() - frame::sender_sp_offset);
4141
}
4242

43+
static inline void patch_return_pc_with_preempt_stub(frame& f) {
44+
if (f.is_runtime_frame()) {
45+
// Unlike x86 we don't know where in the callee frame the return pc is
46+
// saved so we can't patch the return from the VM call back to Java.
47+
// Instead, we will patch the return from the runtime stub back to the
48+
// compiled method so that the target returns to the preempt cleanup stub.
49+
intptr_t* caller_sp = f.sp() + f.cb()->frame_size();
50+
caller_sp[-1] = (intptr_t)StubRoutines::cont_preempt_stub();
51+
} else {
52+
// The target will check for preemption once it returns to the interpreter
53+
// or the native wrapper code and will manually jump to the preempt stub.
54+
JavaThread *thread = JavaThread::current();
55+
thread->set_preempt_alternate_return(StubRoutines::cont_preempt_stub());
56+
}
57+
}
58+
4359
inline int ContinuationHelper::frame_align_words(int size) {
4460
#ifdef _LP64
4561
return size & 1;
@@ -83,12 +99,12 @@ inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor,
8399
anchor->set_last_Java_fp(entry->entry_fp());
84100
}
85101

86-
#ifdef ASSERT
87102
inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) {
88103
intptr_t* fp = *(intptr_t**)(sp - frame::sender_sp_offset);
89104
anchor->set_last_Java_fp(fp);
90105
}
91106

107+
#ifdef ASSERT
92108
inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) {
93109
intptr_t* sp = f.sp();
94110
address pc = ContinuationHelper::return_address_at(

src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
666666
{
667667
assert(lock_reg == c_rarg1, "The argument is only for looks. It must be c_rarg1");
668668
if (LockingMode == LM_MONITOR) {
669-
call_VM(noreg,
669+
call_VM_preemptable(noreg,
670670
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
671671
lock_reg);
672672
} else {
@@ -756,7 +756,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
756756
bind(slow_case);
757757

758758
// Call the runtime routine for slow case
759-
call_VM(noreg,
759+
call_VM_preemptable(noreg,
760760
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
761761
lock_reg);
762762

@@ -1531,6 +1531,54 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result,
15311531
restore_locals();
15321532
}
15331533

1534+
void InterpreterMacroAssembler::call_VM_preemptable(Register oop_result,
1535+
address entry_point,
1536+
Register arg_1) {
1537+
assert(arg_1 == c_rarg1, "");
1538+
Label resume_pc, not_preempted;
1539+
1540+
#ifdef ASSERT
1541+
{
1542+
Label L;
1543+
ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset()));
1544+
cbz(rscratch1, L);
1545+
stop("Should not have alternate return address set");
1546+
bind(L);
1547+
}
1548+
#endif /* ASSERT */
1549+
1550+
push_cont_fastpath();
1551+
1552+
// Make VM call. In case of preemption set last_pc to the one we want to resume to.
1553+
adr(rscratch1, resume_pc);
1554+
str(rscratch1, Address(rthread, JavaThread::last_Java_pc_offset()));
1555+
call_VM_base(oop_result, noreg, noreg, entry_point, 1, false /*check_exceptions*/);
1556+
1557+
pop_cont_fastpath();
1558+
1559+
// Check if preempted.
1560+
ldr(rscratch1, Address(rthread, JavaThread::preempt_alternate_return_offset()));
1561+
cbz(rscratch1, not_preempted);
1562+
str(zr, Address(rthread, JavaThread::preempt_alternate_return_offset()));
1563+
br(rscratch1);
1564+
1565+
// In case of preemption, this is where we will resume once we finally acquire the monitor.
1566+
bind(resume_pc);
1567+
restore_after_resume(false /* is_native */);
1568+
1569+
bind(not_preempted);
1570+
}
1571+
1572+
void InterpreterMacroAssembler::restore_after_resume(bool is_native) {
1573+
lea(rscratch1, ExternalAddress(Interpreter::cont_resume_interpreter_adapter()));
1574+
blr(rscratch1);
1575+
if (is_native) {
1576+
// On resume we need to set up stack as expected
1577+
push(dtos);
1578+
push(ltos);
1579+
}
1580+
}
1581+
15341582
void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) {
15351583
assert_different_registers(obj, rscratch1, mdo_addr.base(), mdo_addr.index());
15361584
Label update, next, none;

src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ class InterpreterMacroAssembler: public MacroAssembler {
5858

5959
void load_earlyret_value(TosState state);
6060

61+
void call_VM_preemptable(Register oop_result,
62+
address entry_point,
63+
Register arg_1);
64+
void restore_after_resume(bool is_native);
65+
6166
void jump_to_entry(address entry);
6267

6368
virtual void check_and_handle_popframe(Register java_thread);

src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,10 @@ static void pass_arg3(MacroAssembler* masm, Register arg) {
776776
}
777777
}
778778

779+
static bool is_preemptable(address entry_point) {
780+
return entry_point == CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter);
781+
}
782+
779783
void MacroAssembler::call_VM_base(Register oop_result,
780784
Register java_thread,
781785
Register last_java_sp,
@@ -811,7 +815,12 @@ void MacroAssembler::call_VM_base(Register oop_result,
811815
assert(last_java_sp != rfp, "can't use rfp");
812816

813817
Label l;
814-
set_last_Java_frame(last_java_sp, rfp, l, rscratch1);
818+
if (is_preemptable(entry_point)) {
819+
// skip setting last_pc since we already set it to desired value.
820+
set_last_Java_frame(last_java_sp, rfp, noreg, rscratch1);
821+
} else {
822+
set_last_Java_frame(last_java_sp, rfp, l, rscratch1);
823+
}
815824

816825
// do the call, remove parameters
817826
MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments, &l);

src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,8 +938,9 @@ class MacroAssembler: public Assembler {
938938
void pop_CPU_state(bool restore_vectors = false, bool use_sve = false,
939939
int sve_vector_size_in_bytes = 0, int total_predicate_in_bytes = 0);
940940

941-
void push_cont_fastpath(Register java_thread);
942-
void pop_cont_fastpath(Register java_thread);
941+
void push_cont_fastpath(Register java_thread = rthread);
942+
void pop_cont_fastpath(Register java_thread = rthread);
943+
943944
void inc_held_monitor_count();
944945
void dec_held_monitor_count();
945946

0 commit comments

Comments
 (0)