From c1564a2aa4ab5e546200cb92a8c70949ab40ab1d Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Wed, 26 Oct 2022 08:29:34 +0200 Subject: [PATCH 01/11] Loom ppc64le port --- .../cpu/aarch64/continuationEntry_aarch64.hpp | 32 + .../continuationFreezeThaw_aarch64.inline.hpp | 6 + .../continuationHelper_aarch64.inline.hpp | 3 +- src/hotspot/cpu/aarch64/frame_aarch64.hpp | 7 + src/hotspot/cpu/arm/continuationEntry_arm.hpp | 32 + .../arm/continuationFreezeThaw_arm.inline.hpp | 5 + .../cpu/arm/continuationHelper_arm.inline.hpp | 2 +- src/hotspot/cpu/arm/frame_arm.hpp | 7 + src/hotspot/cpu/ppc/assembler_ppc.hpp | 6 +- src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 2 +- src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp | 4 + src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 6 +- src/hotspot/cpu/ppc/continuationEntry_ppc.hpp | 35 + .../cpu/ppc/continuationEntry_ppc.inline.hpp | 16 +- .../ppc/continuationFreezeThaw_ppc.inline.hpp | 588 ++++++++++- .../cpu/ppc/continuationHelper_ppc.inline.hpp | 131 ++- src/hotspot/cpu/ppc/frame_ppc.cpp | 48 +- src/hotspot/cpu/ppc/frame_ppc.hpp | 43 +- src/hotspot/cpu/ppc/frame_ppc.inline.hpp | 225 ++-- src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp | 2 + src/hotspot/cpu/ppc/globals_ppc.hpp | 2 +- src/hotspot/cpu/ppc/interp_masm_ppc.hpp | 2 +- src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 22 +- src/hotspot/cpu/ppc/macroAssembler_ppc.cpp | 103 +- src/hotspot/cpu/ppc/macroAssembler_ppc.hpp | 8 + src/hotspot/cpu/ppc/nativeInst_ppc.cpp | 32 +- src/hotspot/cpu/ppc/nativeInst_ppc.hpp | 37 +- src/hotspot/cpu/ppc/ppc.ad | 12 +- src/hotspot/cpu/ppc/register_ppc.hpp | 4 +- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 441 +++++++- .../cpu/ppc/smallRegisterMap_ppc.inline.hpp | 24 +- .../ppc/stackChunkFrameStream_ppc.inline.hpp | 140 ++- .../cpu/ppc/stackChunkOop_ppc.inline.hpp | 10 +- src/hotspot/cpu/ppc/stubGenerator_ppc.cpp | 117 ++- .../ppc/templateInterpreterGenerator_ppc.cpp | 17 +- src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 2 + .../cpu/riscv/continuationEntry_riscv.hpp | 32 + .../continuationFreezeThaw_riscv.inline.hpp | 5 + .../riscv/continuationHelper_riscv.inline.hpp | 2 +- src/hotspot/cpu/riscv/frame_riscv.hpp | 7 + .../cpu/s390/continuationEntry_s390.hpp | 32 + .../continuationFreezeThaw_s390.inline.hpp | 5 + .../s390/continuationHelper_s390.inline.hpp | 2 +- src/hotspot/cpu/s390/frame_s390.hpp | 10 +- src/hotspot/cpu/x86/continuationEntry_x86.hpp | 32 + .../x86/continuationFreezeThaw_x86.inline.hpp | 6 + .../cpu/x86/continuationHelper_x86.inline.hpp | 3 +- src/hotspot/cpu/x86/frame_x86.hpp | 10 +- .../cpu/zero/continuationEntry_zero.hpp | 32 + .../continuationFreezeThaw_zero.inline.hpp | 5 + .../zero/continuationHelper_zero.inline.hpp | 2 +- src/hotspot/cpu/zero/frame_zero.hpp | 7 + .../share/oops/instanceStackChunkKlass.hpp | 84 +- .../oops/instanceStackChunkKlass.inline.hpp | 4 +- src/hotspot/share/oops/stackChunkOop.cpp | 8 +- .../share/oops/stackChunkOop.inline.hpp | 8 +- src/hotspot/share/runtime/continuation.cpp | 2 +- .../share/runtime/continuationEntry.hpp | 5 + .../runtime/continuationEntry.inline.hpp | 4 +- .../share/runtime/continuationFreezeThaw.cpp | 118 ++- .../share/runtime/continuationHelper.hpp | 2 +- .../runtime/continuationWrapper.inline.hpp | 5 + src/hotspot/share/runtime/frame.cpp | 9 +- src/hotspot/share/runtime/sharedRuntime.cpp | 10 +- .../runtime/stackChunkFrameStream.inline.hpp | 52 +- .../internal/vm/Continuation/BasicExp.java | 976 ++++++++++++++++++ 66 files changed, 3250 insertions(+), 402 deletions(-) create mode 100644 src/hotspot/cpu/aarch64/continuationEntry_aarch64.hpp create mode 100644 src/hotspot/cpu/arm/continuationEntry_arm.hpp create mode 100644 src/hotspot/cpu/ppc/continuationEntry_ppc.hpp create mode 100644 src/hotspot/cpu/riscv/continuationEntry_riscv.hpp create mode 100644 src/hotspot/cpu/s390/continuationEntry_s390.hpp create mode 100644 src/hotspot/cpu/x86/continuationEntry_x86.hpp create mode 100644 src/hotspot/cpu/zero/continuationEntry_zero.hpp create mode 100644 test/jdk/jdk/internal/vm/Continuation/BasicExp.java diff --git a/src/hotspot/cpu/aarch64/continuationEntry_aarch64.hpp b/src/hotspot/cpu/aarch64/continuationEntry_aarch64.hpp new file mode 100644 index 0000000000000..532fe6efe6b59 --- /dev/null +++ b/src/hotspot/cpu/aarch64/continuationEntry_aarch64.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_AARCH64_CONTINUATIONENTRY_AARCH64_HPP +#define CPU_AARCH64_CONTINUATIONENTRY_AARCH64_HPP + +class ContinuationEntryPD { + // empty +}; + +#endif // CPU_AARCH64_CONTINUATIONENTRY_AARCH64_HPP diff --git a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp index c393426dcd0ab..79976b84d0a66 100644 --- a/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationFreezeThaw_aarch64.inline.hpp @@ -201,6 +201,12 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) { *(intptr_t**)(sp - frame::sender_sp_offset) = fp; } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + // Fast path depends on !PreserveFramePointer. See can_thaw_fast(). + assert(!PreserveFramePointer, "Frame pointers need to be fixed"); +} + // Slow path inline frame ThawBase::new_entry_frame() { diff --git a/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp index 474090277175e..3b070c00359a5 100644 --- a/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp @@ -101,7 +101,8 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return (address*)(f.fp() + frame::return_addr_offset); } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { + intptr_t* sp = caller.unextended_sp(); assert(f.is_interpreted_frame(), ""); intptr_t* la = f.addr_at(frame::interpreter_frame_sender_sp_offset); *la = f.is_heap_frame() ? (intptr_t)(sp - f.fp()) : (intptr_t)sp; diff --git a/src/hotspot/cpu/aarch64/frame_aarch64.hpp b/src/hotspot/cpu/aarch64/frame_aarch64.hpp index 81e85318b4669..3d1f588359fa8 100644 --- a/src/hotspot/cpu/aarch64/frame_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/frame_aarch64.hpp @@ -101,6 +101,13 @@ // size, in words, of frame metadata (e.g. pc and link) metadata_words = sender_sp_offset, + // size, in words, of metadata at frame bottom, i.e. it is not part of the + // caller/callee overlap + metadata_words_at_bottom = metadata_words, + // size, in words, of frame metadata at the frame top, i.e. it is located + // between a callee frame and its stack arguments, where it is part + // of the caller/callee overlap + metadata_words_at_top = 0, // in bytes frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment diff --git a/src/hotspot/cpu/arm/continuationEntry_arm.hpp b/src/hotspot/cpu/arm/continuationEntry_arm.hpp new file mode 100644 index 0000000000000..fdb8cf6326ce9 --- /dev/null +++ b/src/hotspot/cpu/arm/continuationEntry_arm.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_ARM_CONTINUATIONENTRY_ARM_HPP +#define CPU_ARM_CONTINUATIONENTRY_ARM_HPP + +class ContinuationEntryPD { + // empty +}; + +#endif // CPU_ARM_CONTINUATIONENTRY_ARM_HPP diff --git a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp index 1c2f75b9e3566..c166ca72254d1 100644 --- a/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationFreezeThaw_arm.inline.hpp @@ -91,6 +91,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) { Unimplemented(); } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + Unimplemented(); +} + inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Unimplemented(); } diff --git a/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp b/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp index bf194b1e46928..4a9a7fe8e57cb 100644 --- a/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp @@ -89,7 +89,7 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return NULL; } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { Unimplemented(); } diff --git a/src/hotspot/cpu/arm/frame_arm.hpp b/src/hotspot/cpu/arm/frame_arm.hpp index f3b31fadf16b1..7ef941ad0ff78 100644 --- a/src/hotspot/cpu/arm/frame_arm.hpp +++ b/src/hotspot/cpu/arm/frame_arm.hpp @@ -54,6 +54,13 @@ // Entry frames entry_frame_call_wrapper_offset = 0, metadata_words = sender_sp_offset, + // size, in words, of metadata at frame bottom, i.e. it is not part of the + // caller/callee overlap + metadata_words_at_bottom = metadata_words, + // size, in words, of frame metadata at the frame top, i.e. it is located + // between a callee frame and its stack arguments, where it is part + // of the caller/callee overlap + metadata_words_at_top = 0, frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 3f110fe5f2907..9449276bfe6d2 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -141,8 +141,8 @@ class Argument { // in a register. This is not documented, but we follow this convention, too. n_regs_not_on_stack_c = 8, - n_int_register_parameters_j = 8, - n_float_register_parameters_j = 13 + n_int_register_parameters_j = 8, // duplicates num_java_iarg_registers + n_float_register_parameters_j = 13, // num_java_farg_registers }; // creation Argument(int number) : _number(number) {} @@ -1372,7 +1372,7 @@ class Assembler : public AbstractAssembler { // Issue an illegal instruction. inline void illtrap(); - static inline bool is_illtrap(int x); + static inline bool is_illtrap(address instr_addr); // PPC 1, section 3.3.8, Fixed-Point Arithmetic Instructions inline void addi( Register d, Register a, int si16); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index be6f2408a2670..fc5df27148ae5 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -79,7 +79,7 @@ inline address Assembler::emit_fd(address entry, address toc, address env) { // Issue an illegal instruction. 0 is guaranteed to be an illegal instruction. inline void Assembler::illtrap() { Assembler::emit_int32(0); } -inline bool Assembler::is_illtrap(int x) { return x == 0; } +inline bool Assembler::is_illtrap(address instr_addr) { return *(uint32_t*)instr_addr == 0u; } // PPC 1, section 3.3.8, Fixed-Point Arithmetic Instructions inline void Assembler::addi( Register d, Register a, int si16) { assert(a != R0, "r0 not allowed"); addi_r0ok( d, a, si16); } diff --git a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp index da1cecf3e8a5a..ea676110f97c8 100644 --- a/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_LIRAssembler_ppc.cpp @@ -665,6 +665,7 @@ void LIR_Assembler::call(LIR_OpJavaCall* op, relocInfo::relocType rtype) { __ code()->set_insts_mark(); __ bl(__ pc()); add_call_info(code_offset(), op->info()); + __ post_call_nop(); } @@ -692,6 +693,7 @@ void LIR_Assembler::ic_call(LIR_OpJavaCall* op) { // serves as dummy and the bl will be patched later. __ bl(__ pc()); add_call_info(code_offset(), op->info()); + __ post_call_nop(); } void LIR_Assembler::explicit_null_check(Register addr, CodeEmitInfo* info) { @@ -2876,6 +2878,7 @@ void LIR_Assembler::rt_call(LIR_Opr result, address dest, __ bctrl(); assert(info != NULL, "sanity"); add_call_info_here(info); + __ post_call_nop(); return; } @@ -2883,6 +2886,7 @@ void LIR_Assembler::rt_call(LIR_Opr result, address dest, if (info != NULL) { add_call_info_here(info); } + __ post_call_nop(); } diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp index 94b39042c7157..9db62b93a955b 100644 --- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp @@ -149,6 +149,8 @@ void C1_MacroAssembler::lock_object(Register Rmark, Register Roop, Register Rbox bne(CCR0, slow_int); bind(done); + + inc_held_monitor_count(Rmark /*tmp*/); } @@ -160,7 +162,7 @@ void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rb Address mark_addr(Roop, oopDesc::mark_offset_in_bytes()); assert(mark_addr.disp() == 0, "cas must take a zero displacement"); - // Test first it it is a fast recursive unlock. + // Test first if it is a fast recursive unlock. ld(Rmark, BasicLock::displaced_header_offset_in_bytes(), Rbox); cmpdi(CCR0, Rmark, 0); beq(CCR0, done); @@ -186,6 +188,8 @@ void C1_MacroAssembler::unlock_object(Register Rmark, Register Roop, Register Rb // Done bind(done); + + dec_held_monitor_count(Rmark /*tmp*/); } diff --git a/src/hotspot/cpu/ppc/continuationEntry_ppc.hpp b/src/hotspot/cpu/ppc/continuationEntry_ppc.hpp new file mode 100644 index 0000000000000..ae912b15ceef8 --- /dev/null +++ b/src/hotspot/cpu/ppc/continuationEntry_ppc.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_PPC_CONTINUATIONENTRY_PPC_HPP +#define CPU_PPC_CONTINUATIONENTRY_PPC_HPP + +#include "runtime/frame.hpp" + +class ContinuationEntryPD { + // This is needed to position the ContinuationEntry at the unextended sp of the entry frame + frame::abi_reg_args _abi; +}; + +#endif // CPU_PPC_CONTINUATIONENTRY_PPC_HPP diff --git a/src/hotspot/cpu/ppc/continuationEntry_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationEntry_ppc.inline.hpp index c38ccd842ac58..1d2749724c5d9 100644 --- a/src/hotspot/cpu/ppc/continuationEntry_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationEntry_ppc.inline.hpp @@ -27,20 +27,24 @@ #include "runtime/continuationEntry.hpp" -// TODO: Implement +#include "oops/method.inline.hpp" +#include "runtime/frame.inline.hpp" +#include "runtime/registerMap.hpp" +#include "utilities/macros.hpp" inline frame ContinuationEntry::to_frame() const { - Unimplemented(); - return frame(); + static CodeBlob* cb = CodeCache::find_blob_fast(entry_pc()); + assert(cb != nullptr, ""); + assert(cb->as_compiled_method()->method()->is_continuation_enter_intrinsic(), ""); + return frame(entry_sp(), entry_pc(), entry_sp(), entry_fp(), cb); } inline intptr_t* ContinuationEntry::entry_fp() const { - Unimplemented(); - return nullptr; + return (intptr_t*)((address)this + size()); } inline void ContinuationEntry::update_register_map(RegisterMap* map) const { - Unimplemented(); +// Nothing to do (no non-volatile registers in java calling convention) } #endif // CPU_PPC_CONTINUATIONENTRY_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp index 8753c4c8e5e29..ea0690ea45acc 100644 --- a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp @@ -29,71 +29,599 @@ #include "runtime/frame.hpp" #include "runtime/frame.inline.hpp" +inline void patch_callee_link(const frame& f, intptr_t* fp) { + *ContinuationHelper::Frame::callee_link_address(f) = fp; +} -inline void FreezeBase::set_top_frame_metadata_pd(const frame& hf) { - Unimplemented(); +inline void patch_callee_link_relative(const frame& f, intptr_t* fp) { + intptr_t* la = (intptr_t*)ContinuationHelper::Frame::callee_link_address(f); + intptr_t new_value = fp - la; + *la = new_value; +} + +////// Freeze + +// Fast path + +inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { + // Nothing to do. The backchain is reconstructed when thawing (see Thaw::patch_caller_links()) } +// Slow path + template inline frame FreezeBase::sender(const frame& f) { - Unimplemented(); - return frame(); -} + assert(FKind::is_instance(f), ""); + if (FKind::interpreted) { + return frame(f.sender_sp(), f.sender_pc(), f.interpreter_frame_sender_sp()); + } + + intptr_t* sender_sp = f.sender_sp(); + address sender_pc = f.sender_pc(); + assert(sender_sp != f.sp(), "must have changed"); -template frame FreezeBase::new_heap_frame(frame& f, frame& caller) { - Unimplemented(); - return frame(); + int slot = 0; + CodeBlob* sender_cb = CodeCache::find_blob_and_oopmap(sender_pc, slot); + return sender_cb != nullptr + ? frame(sender_sp, sender_sp, nullptr, sender_pc, sender_cb, slot == -1 ? nullptr : sender_cb->oop_map_for_slot(slot, sender_pc)) + : frame(sender_sp, sender_pc, sender_sp); } void FreezeBase::adjust_interpreted_frame_unextended_sp(frame& f) { - Unimplemented(); + // nothing to do +} + +static inline void relativize_one(intptr_t* const vfp, intptr_t* const hfp, int offset) { + assert(*(hfp + offset) == *(vfp + offset), ""); + intptr_t* addr = hfp + offset; + intptr_t value = *(intptr_t**)addr - vfp; + *addr = value; } inline void FreezeBase::relativize_interpreted_frame_metadata(const frame& f, const frame& hf) { - Unimplemented(); + intptr_t* vfp = f.fp(); + intptr_t* hfp = hf.fp(); + assert(f.fp() > (intptr_t*)f.interpreter_frame_esp(), ""); + + // There is alignment padding between vfp and f's locals array in the original + // frame, therefore we cannot use it to relativize the locals pointer. + *hf.addr_at(ijava_idx(locals)) = frame::metadata_words + f.interpreter_frame_method()->max_locals() - 1; + relativize_one(vfp, hfp, ijava_idx(monitors)); + relativize_one(vfp, hfp, ijava_idx(esp)); + relativize_one(vfp, hfp, ijava_idx(top_frame_sp)); + + // hfp == hf.sp() + (f.fp() - f.sp()) is not true on ppc because the stack frame has room for + // the maximal expression stack and the expression stack in the heap frame is trimmed. + assert(hf.fp() == hf.interpreter_frame_esp() + (f.fp() - f.interpreter_frame_esp()), ""); + assert(hf.fp() <= (intptr_t*)hf.at(ijava_idx(locals)), ""); +} + +inline void FreezeBase::set_top_frame_metadata_pd(const frame& hf) { + stackChunkOop chunk = _cont.tail(); + assert(chunk->is_in_chunk(hf.sp()), "hf.sp()=" PTR_FORMAT, p2i(hf.sp())); + + hf.own_abi()->lr = (uint64_t)hf.pc(); + if (hf.is_interpreted_frame()) { + patch_callee_link_relative(hf, hf.fp()); + } +#ifdef ASSERT + else { + // See also FreezeBase::patch_pd() + patch_callee_link(hf, (intptr_t*)badAddress); + } +#endif +} + +// +// Heap frames differ from stack frames in the following aspects +// +// - they are just word aligned +// - the unextended sp of interpreted frames is set such that +// unextended sp + frame::metadata_words_at_top + 1 points to the last call parameter +// (the comment at the file end explains the unextended sp for interpreted frames on the stack) +// +// The difference in respect to the unextended sp is required to comply with shared code. +// Furthermore fast frozen and compiled frames have invalid back links (see +// Thaw::patch_caller_links() and FreezeBase::patch_pd()) +// +// === New Interpreted Frame ========================================================================================== +// +// ### Interpreted Caller: Overlap new frame with Caller +// +// Caller on entry New frame with resized Caller +// +// | frame::abi_minframe | | | +// | |<- FP of caller | Caller's SP |<- FP of caller +// ========================== ========================== +// | ijava_state | | ijava_state | +// | | | | +// |------------------------| ----- |------------------------| +// | P0 | ^ | L0 aka P0 | +// | : | | | : : | +// | Pn |<- unext. SP | | : Pn |<- unext. SP +// |------------------------| + metadata overlap | : | + metadata +// | frame::abi_minframe | | | Lm | +// | (metadata_words_at_top)|<- SP == unext. SP v |------------------------|<- unextended SP of caller (1) +// ========================== of caller ----- | frame::abi_minframe | +// | (metadata_words_at_top)|<- new SP of caller / FP of new frame +// overlap = stack_argsize(f) ========================== ^ +// + frame::metadata_words_at_top | ijava_state | | +// | | | +// Where f is the frame to be relocated on the heap. |------------------------| | +// See also StackChunkFrameStream::frame_size(). | Expressions | FP - esp of f +// | P0 | | +// | : | | +// | Growth | | Pi | v +// v v |------------------------| --- +// | frame::abi_minframe | +// | (metadata_words_at_top)|<- unextended SP / +// ========================== SP of new frame +// ### Compiled Caller: No Overlap +// +// The caller is resized to accomodate the callee's locals and abi but there is _no_ overlap with +// the original caller frame. +// +// Caller on entry New frame with resized Caller +// +// | frame::abi_minframe | | | +// | (metadata_words_at_top)|<- FP of caller | Caller's SP |<- FP of caller +// ========================== ========================== +// | | | | +// | | | | +// |------------------------| |------------------------| +// | frame::abi_minframe | | frame::abi_minframe | +// | (metadata_words_at_top)|<- SP == unext. SP | (metadata_words_at_top)|<- unext. SP of caller +// ========================== of caller |------------------------| +// | L0 aka P0 | +// | : : | +// | : Pn | +// overlap = 0 | Lm | +// |------------------------| +// f is the frame to be relocated on the heap | frame::abi_minframe | +// | (metadata_words_at_top)|<- new SP of caller / FP of new frame +// ========================== ^ +// | ijava_state | | +// | Growth | | | | +// v v |------------------------| | +// | Expressions | FP - esp of f +// | P0 | | +// | : | | +// | Pi | v +// |------------------------| --- +// | frame::abi_minframe | +// | (metadata_words_at_top)|<- unextended SP / +// ========================== SP of new frame +// +// (1) Caller's unextended SP is preserved in callee's frame::ijava_state::sender_sp +// (See ContinuationHelper::InterpretedFrame::patch_sender_sp). This is required +// by StackChunkFrameStream::next_for_interpreter_frame(). +// +// === New Compiled Frame ============================================================================================= +// +// ### Interpreted Caller: No Overlap +// +// The caller is resized to accomodate the callee's stack arguments and abi but there is _no_ overlap with +// the original caller frame. +// +// Note: a new ABI is added to the caller even if there are no stackargs. +// This is necessary to comply with shared code. +// +// Caller on entry New frame with resized Caller +// +// | frame::abi_minframe | | frame::abi_minframe | +// | (metadata_words_at_top)|<- FP of caller | (metadata_words_at_top)|<- FP of caller +// ========================== ========================== +// | ijava_state | | ijava_state | +// | | | | +// |------------------------| |------------------------| +// | P0 | | P0 | +// | : | | : | +// | Pn |<- unext. SP | Pn |<- unext. SP +// |------------------------| + metadata |------------------------| + metadata +// | frame::abi_minframe | | frame::abi_minframe | +// | (metadata_words_at_top)|<- SP == unext. SP | (metadata_words_at_top)|<- unextended SP of caller (1) +// ========================== of caller |------------------------| +// | Stack Args | +// overlap = 0 | (if any) | +// |------------------------| +// f is the frame to be relocated on the heap | frame::abi_minframe | +// | (metadata_words_at_top)|<- new SP of caller / FP of new frame +// ========================== +// | | +// | Growth | | | +// v v |------------------------| +// | frame::abi_minframe | +// | (metadata_words_at_top)|<- SP == unext. SP of new frame +// ========================== +// +// ### Compiled Caller: Stackargs + ABI Overlap +// +// Caller on entry New frame with resized Caller +// +// | frame::abi_minframe | | frame::abi_minframe | +// | (metadata_words_at_top)|<- FP of caller | (metadata_words_at_top)|<- FP of caller +// ========================== ========================== +// | | | | +// | | | | +// |------------------------| ----- |------------------------| +// | Stack Args | ^ | Stack Args | +// | (if any) | | | (if any) | +// |------------------------| overlap |------------------------| +// | frame::abi_minframe | | | frame::abi_minframe | +// | (metadata_words_at_top)|<- SP == unext. SP v | (metadata_words_at_top)|<- SP == unext. SP of caller +// ========================== of caller ----- ========================== / FP of new frame +// | | +// overlap = stack_argsize(f) | | +// + frame::metadata_words_at_top |------------------------| +// | frame::abi_minframe | +// Where f is the frame to be relocated on the heap. | (metadata_words_at_top)|<- SP == unext. SP of new frame +// See also StackChunkFrameStream::frame_size(). ========================== +// +template +frame FreezeBase::new_heap_frame(frame& f, frame& caller) { + assert(FKind::is_instance(f), ""); + + intptr_t *sp, *fp; + if (FKind::interpreted) { + int locals = f.interpreter_frame_method()->max_locals(); + // If the caller.is_empty(), i.e. we're freezing into an empty chunk, then we set + // the chunk's argsize in finalize_freeze and make room for it above the unextended_sp + // See also comment on StackChunkFrameStream::interpreter_frame_size() + int overlap = + (caller.is_interpreted_frame() || caller.is_empty()) + ? ContinuationHelper::InterpretedFrame::stack_argsize(f) + frame::metadata_words_at_top + : 0; + fp = caller.unextended_sp() + overlap - locals - frame::metadata_words_at_top; + // esp points one slot below the last argument + intptr_t* x86_64_like_unextended_sp = f.interpreter_frame_esp() + 1 - frame::metadata_words_at_top; + sp = fp - (f.fp() - x86_64_like_unextended_sp); + + assert (sp <= fp && (fp <= caller.unextended_sp() || caller.is_interpreted_frame()), + "sp=" PTR_FORMAT " fp=" PTR_FORMAT " caller.unextended_sp()=" PTR_FORMAT " caller.is_interpreted_frame()=%d", + p2i(sp), p2i(fp), p2i(caller.unextended_sp()), caller.is_interpreted_frame()); + caller.set_sp(fp); + + assert(_cont.tail()->is_in_chunk(sp), ""); + + frame hf(sp, sp, fp, f.pc(), nullptr, nullptr, true /* on_heap */); + // frame_top() and frame_bottom() read these before relativize_interpreted_frame_metadata() is called + *hf.addr_at(ijava_idx(locals)) = frame::metadata_words + locals - 1; + *hf.addr_at(ijava_idx(esp)) = f.interpreter_frame_esp() - f.fp(); + return hf; + } else { + int fsize = FKind::size(f); + sp = caller.unextended_sp() - fsize; + if (caller.is_interpreted_frame()) { + // If the caller is interpreted, our stackargs are not supposed to overlap with it + // so we make more room by moving sp down by argsize + int argsize = FKind::stack_argsize(f); + sp -= argsize + frame::metadata_words_at_top; + } + fp = sp + fsize; + caller.set_sp(fp); + + assert(_cont.tail()->is_in_chunk(sp), ""); + + return frame(sp, sp, fp, f.pc(), nullptr, nullptr, true /* on_heap */); + } } inline void FreezeBase::patch_pd(frame& hf, const frame& caller) { - Unimplemented(); + if (caller.is_interpreted_frame()) { + assert(!caller.is_empty(), ""); + patch_callee_link_relative(caller, caller.fp()); + } +#ifdef ASSERT + else { + // For compiled frames the back link is actually redundant. It gets computed + // as unextended_sp + frame_size. + + // Note the difference on x86_64: the link is not made relative if the caller + // is a compiled frame because there rbp is used as a non-volatile register by + // c1/c2 so it could be a computed value local to the caller. + + // See also: + // - FreezeBase::set_top_frame_metadata_pd + // - StackChunkFrameStream::fp() + // - UseContinuationFastPath: compiled frames are copied in a batch w/o patching the back link. + // The backlinks are restored when thawing (see Thaw::patch_caller_links()) + patch_callee_link(hf, (intptr_t*)badAddress); + } +#endif } -inline void FreezeBase::patch_stack_pd(intptr_t* frame_sp, intptr_t* heap_sp) { - Unimplemented(); +//////// Thaw + +// Fast path + +inline void ThawBase::prefetch_chunk_pd(void* start, int size) { + size <<= LogBytesPerWord; + Prefetch::read(start, size); + Prefetch::read(start, size - 64); } +// Set back chain links of fast thawed frames such that *sp == callers_sp. +// See https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#STACK +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + for (intptr_t* callers_sp; sp < bottom; sp = callers_sp) { + address pc = (address)((frame::abi_minframe*) sp)->lr; + assert(pc != nullptr, ""); + // see ThawBase::patch_return() which gets called just before + bool is_entry_frame = pc == StubRoutines::cont_returnBarrier() || pc == _cont.entryPC(); + if (is_entry_frame) { + callers_sp = _cont.entryFP(); + } else { + CodeBlob* cb = CodeCache::find_blob(pc); + callers_sp = sp + cb->frame_size(); + } + // set the back link + ((frame::abi_minframe*) sp)->callers_sp = (intptr_t) callers_sp; + } +} + +// Slow path + inline frame ThawBase::new_entry_frame() { - Unimplemented(); - return frame(); + intptr_t* sp = _cont.entrySP(); + return frame(sp, _cont.entryPC(), sp, _cont.entryFP()); } +// === New Interpreted Frame ================================================================================================================ +// +// ### Non-Interpreted Caller (compiled, enterSpecial): No Overlap +// +// Heap Frame `hf` `hf` gets copied to stack _without_ overlapping the caller +// +// | | Non-Interpreted | | +// | |<- bottom Caller |----------------------| +// |----------------------| ^ | frame::abi_minframe |<- unextended SP +// | L0 aka P0 | | --- ======================== +// | : : | | ^ | L0 aka P0 | +// | : Pn | | | | : : | Parameters do +// | : | | | | : Pn | not overlap with +// | Lm | | | | : | caller! +// |----------------------| `fsize` | | : | +// | frame::abi_minframe | | | : | +// ======================== | `fsize` + padding | Lm | +// | | | |----------------------| +// | ijava_state | | | | Opt. Align. Padding | +// | | | | |----------------------| +// |----------------------| | | | frame::abi_minframe |<- new SP of caller +// | L0 aka P0 | | | ======================== / FP of new frame +// | : : | | | | | (aligned) +// | : Pn |<- unext. SP + metadata | | ijava_state | +// | : | | | | | +// | Lm | | | |----------------------| +// |----------------------| v | | P0 | +// | frame::abi_minframe |<- SP / unextended SP | | : | +// ======================== | | Pi |<- unextended SP + metadata +// | |----------------------| +// | Growth | v | frame::abi_minframe |<- unextended SP / SP of new frame +// v v --- ======================== (not yet aligned(1)) +// +// +// ### Interpreted Caller: Overlap with Caller +// +// Caller New frame with resized/aligned Caller +// +// | | | | +// | ijava_state | | ijava_state | +// |----------------------| |----------------------| +// | non param. expr. | bottom | non param. expr. | +// | - - - - - - - - - - | --- ^ | - - - - - - - - - - | +// | P0 | ^ | | L0 aka P0 | +// | : | | | | : : | +// | Pn |<- unextended SP overlap | | : Pn |<- unextended SP +// |----------------------| + metadata_words_at_top | | | : | + metadata_words_at_top +// | frame::abi_minframe |<- unextended SP v | | : | (unaligned) +// ======================== / SP of new frame --- | | : | of caller +// (not yet aligned(1)) | | Lm | +// `fsize` |----------------------| +// overlap = stack_argsize(hf) + padding| Opt. Align. Padding | +// + frame::metadata_words_at_top | |----------------------| +// | | frame::abi_minframe |<- new SP of caller +// | ======================== / FP of new frame +// | | | (aligned) +// | Growth | | | ijava_state | +// v v | | | +// | |----------------------| +// | | P0 | +// | | : | +// | | Pi |<- unextended SP +// | |----------------------| + metadata_words_at_top +// v | frame::abi_minframe |<- unextended SP / SP of new frame +// --- ======================== (not yet aligned(1)) +// +// +// (1) The SP / unextended SP of the new interpreted frame is not aligned. It +// gets aligned when its callee is pushed on stack or in finish_thaw() if +// it is the top frame. This allows addressing parameters: unextended SP + metadata_words_at_top +// +// (2) If caller is interpreted then its ijava_state::top_frame_sp will be used as sender sp +// of the new frame (see ContinuationHelper::InterpretedFrame::patch_sender_sp() and diagram at the end of this file) +// +// (3) The size of alignment padding required when thawing frames is accounted for +// in FreezeBase::_align_size. +// +// === New Compiled Frame =================================================================================================================== +// +// Compiled Caller Interpreted Caller +// +// - stackargs+abi overlap with caller - gets resized for stackargs +// - no alignment padding - SP gets aligned +// - no overlap with orig. +// caller +// O C +// r a | | | | +// i l | | | | +// g l |----------------------| | | +// i e | Stack Args | | | +// n r | (if any) | |----------------------| +// a |----------------------| | frame::abi_minframe | +// l | frame::abi_minframe |<- unext. SP / SP | (unused) |<- unal.unext.SP +// - - - ======================== - - - - - - - - - - |----------------------|- - - - - - - - - - - - - - - - - - - - - - - - - - - - +// N | | | Opt. Align. Padding | +// e | | |----------------------| +// w |----------------------| | Stack Args | +// | frame::abi_minframe |<- unext. SP / SP | (if any) | +// F ======================== |----------------------| +// r | frame::abi_minframe |<- caller's SP +// a ======================== / new frame's FP +// m | | (aligned) +// e | | +// |----------------------| +// | frame::abi_minframe |<- unext. SP / SP +// ======================== +// +// If the new frame is at the bottom just above the ContinuationEntry frame then the stackargs +// don't overlap the caller either even though it is compiled because the size is not +// limited/known. In contrast to the interpreted caller case the abi overlaps with the caller +// if there are no stackargs. This is to comply with shared code (see e.g. StackChunkFrameStream::frame_size()) +// template frame ThawBase::new_stack_frame(const frame& hf, frame& caller, bool bottom) { - Unimplemented(); - return frame(); -} + assert(FKind::is_instance(hf), ""); -inline void ThawBase::set_interpreter_frame_bottom(const frame& f, intptr_t* bottom) { - Unimplemented(); -} + assert(is_aligned(caller.fp(), frame::frame_alignment), ""); + assert(is_aligned(caller.sp(), frame::frame_alignment), ""); + if (FKind::interpreted) { + // Note: we have to overlap with the caller, at least if it is interpreted, to match the + // max_thawing_size calculation during freeze. See also comment above. + intptr_t* heap_sp = hf.unextended_sp(); + const int fsize = ContinuationHelper::InterpretedFrame::frame_bottom(hf) - hf.unextended_sp(); + const int overlap = !caller.is_interpreted_frame() ? 0 + : ContinuationHelper::InterpretedFrame::stack_argsize(hf) + frame::metadata_words_at_top; + intptr_t* frame_sp = caller.unextended_sp() + overlap - fsize; + intptr_t* fp = frame_sp + (hf.fp() - heap_sp); + // align fp + int padding = fp - align_down(fp, frame::frame_alignment); + fp -= padding; + // alignment of sp is done by callee or in finish_thaw() + frame_sp -= padding; -inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { - Unimplemented(); + // On ppc esp points to the next free slot on the expression stack and sp + metadata points to the last parameter + DEBUG_ONLY(intptr_t* esp = fp + *hf.addr_at(ijava_idx(esp));) + assert(frame_sp + frame::metadata_words_at_top == esp+1, " frame_sp=" PTR_FORMAT " esp=" PTR_FORMAT, p2i(frame_sp), p2i(esp)); + caller.set_sp(fp); + frame f(frame_sp, hf.pc(), frame_sp, fp); + // it's set again later in set_interpreter_frame_bottom, but we need to set the locals now so that + // we could call ContinuationHelper::InterpretedFrame::frame_bottom + intptr_t offset = *hf.addr_at(ijava_idx(locals)) + padding; + assert((int)offset == hf.interpreter_frame_method()->max_locals() + frame::metadata_words_at_top + padding - 1, ""); + *(intptr_t**)f.addr_at(ijava_idx(locals)) = fp + offset; + + return f; + } else { + int fsize = FKind::size(hf); + int argsize = hf.compiled_frame_stack_argsize(); + intptr_t* frame_sp = caller.sp() - fsize; + + if ((bottom && argsize > 0) || caller.is_interpreted_frame()) { + frame_sp -= argsize + frame::metadata_words_at_top; + frame_sp = align_down(frame_sp, frame::alignment_in_bytes); + caller.set_sp(frame_sp + fsize); + } + + assert(hf.cb() != nullptr, ""); + assert(hf.oop_map() != nullptr, ""); + intptr_t* fp = frame_sp + fsize; + return frame(frame_sp, frame_sp, fp, hf.pc(), hf.cb(), hf.oop_map(), false); + } } inline intptr_t* ThawBase::align(const frame& hf, intptr_t* frame_sp, frame& caller, bool bottom) { - Unimplemented(); + // Unused. Alignment is done directly in new_stack_frame() / finish_thaw(). return NULL; } -inline void ThawBase::patch_pd(frame& f, const frame& caller) { - Unimplemented(); +static inline void derelativize_one(intptr_t* const fp, int offset) { + intptr_t* addr = fp + offset; + *addr = (intptr_t)(fp + *addr); +} + +inline void ThawBase::derelativize_interpreted_frame_metadata(const frame& hf, const frame& f) { + intptr_t* vfp = f.fp(); + + derelativize_one(vfp, ijava_idx(monitors)); + derelativize_one(vfp, ijava_idx(esp)); + derelativize_one(vfp, ijava_idx(top_frame_sp)); } -void ThawBase::patch_chunk_pd(intptr_t* sp) { - Unimplemented(); +inline void ThawBase::set_interpreter_frame_bottom(const frame& f, intptr_t* bottom) { + *(intptr_t**)f.addr_at(ijava_idx(locals)) = bottom - 1; } -inline void ThawBase::prefetch_chunk_pd(void* start, int size) { - Unimplemented(); +inline void ThawBase::patch_pd(frame& f, const frame& caller) { + patch_callee_link(caller, caller.fp()); } +// +// Interpreter Calling Procedure on PPC +// +// Caller Resized Caller before the Call New Callee Frame +// +// - SP/FP are 16 byte aligned. - The unused part of the expression stack - The caller's original SP is passed as +// Padding is added as necessary. is removed sender SP (in R21_sender_SP) also by +// - SP is _not_ used as esp - Slots for the callee's nonparameter locals compiled callers. It is saved in the +// (expression stack pointer) are added. ijava_state::sender_sp slot and +// - Has reserved slots for the - The large ABI is replaced with a minimal restored when returning. +// maximal expression stack ABI. This removes a c2i extension if there +// - Has a larger ABI section on - The original SP was saved in is one. +// top that is required to call ijava_state::top_frame_sp slot. - ijava_state::sender_sp will be set +// C++ code From there it is restored as SP _after_ as the caller's unextended sp when +// returning from a call. This reverts the iterating stack frames +// resizing described above. It is also (see frame::unextended_sp() and +// required to undo potential i2c extensions frame::sender_for_interpreter_frame()) +// if the calle should be compiled. +// - Note that unextended SP < SP +// is possible on ppc. +// +// | Minimal ABI | | Minimal ABI | | Minimal ABI | +// | (frame::abi_minframe)| | (frame::abi_minframe)| | (frame::abi_minframe)| +// | 4 words | | 4 words | | 4 words | +// | Caller's SP |<- FP of caller | Caller's SP |<- FP of caller | Caller's SP |<- FP of caller +// ======================== (aligned) ======================== ======================== +// | frame:: | | frame:: | | frame:: | +// | ijava_state | | ijava_state | | ijava_state | +// | | | | | | +// |----------------------| |----------------------| |----------------------| +// | P0 | | L0 aka P0 | | L0 aka P0 | +// | | | : | | : | +// | Pn | | : Pn | | : Pn | +// |----------------------| | : | | : | +// | | | Lm | | Lm | +// | Reserved Expr. Stack | |----------------------| |----------------------| +// | | | Opt. Alignm. Padding | | Opt. Alignm. Padding | +// | |<- ConstMethod |----------------------| |----------------------| +// |----------------------| ::_max_stack | Minimal ABI | | Minimal ABI | +// | Opt. Alignm. Padding | | (frame::abi_minframe)| | (frame::abi_minframe)| +// |----------------------| | 4 words | | 4 words | +// | Large ABI | | Caller's SP |<- new SP of caller | Caller's SP |<- SP of caller / +// | for C++ calls | ======================== (aligned) ======================== FP of callee +// | (frame::abi_reg_args)| | frame:: | (aligned) +// | | | ijava_state | +// | | | | +// | | |----------------------| +// | | | | +// | Caller's SP |<- SP of caller <- unextended SP | Reserved Expr. Stack |<- unextended SP +// ======================== (aligned) of caller | | of caller +// (aligned) | | +// | | +// | | +// | | +// | |<- ConstMethod +// |----------------------| ::_max_stack +// Resize Caller Push new Callee Frame | Opt. Alignm. Padding | +// --------------------> ------------------------> |----------------------| +// (ABI, expressions, locals) | Large ABI | +// | for C++ calls | +// | (frame::abi_reg_args)| +// | | +// | Growth | | | +// v v | | +// | | +// | Caller's SP |<- SP of callee +// ======================== (aligned) +// +// #endif // CPU_PPC_CONTINUATION_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp index 88bbc44de8455..0fbd1a4f318d7 100644 --- a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp @@ -34,13 +34,11 @@ static inline intptr_t** link_address(const frame& f) { } inline int ContinuationHelper::frame_align_words(int size) { - Unimplemented(); - return 0; + return size & 1; } -inline intptr_t* ContinuationHelper::frame_align_pointer(intptr_t* sp) { - Unimplemented(); - return NULL; +inline intptr_t* ContinuationHelper::frame_align_pointer(intptr_t* p) { + return align_down(p, frame::frame_alignment); } template @@ -53,72 +51,131 @@ inline void ContinuationHelper::update_register_map_with_callee(const frame& f, } inline void ContinuationHelper::push_pd(const frame& f) { - Unimplemented(); + f.own_abi()->callers_sp = (uint64_t)f.fp(); } inline void ContinuationHelper::set_anchor_to_entry_pd(JavaFrameAnchor* anchor, ContinuationEntry* cont) { - Unimplemented(); + // nothing to do } #ifdef ASSERT inline void ContinuationHelper::set_anchor_pd(JavaFrameAnchor* anchor, intptr_t* sp) { - Unimplemented(); + // nothing to do } inline bool ContinuationHelper::Frame::assert_frame_laid_out(frame f) { - Unimplemented(); - return false; + intptr_t* sp = f.sp(); + address pc = *(address*)(sp - frame::sender_sp_ret_address_offset()); + intptr_t* fp = (intptr_t*)f.own_abi()->callers_sp; + assert(f.raw_pc() == pc, "f.ra_pc: " INTPTR_FORMAT " actual: " INTPTR_FORMAT, p2i(f.raw_pc()), p2i(pc)); + assert(f.fp() == fp, "f.fp: " INTPTR_FORMAT " actual: " INTPTR_FORMAT, p2i(f.fp()), p2i(fp)); + return f.raw_pc() == pc && f.fp() == fp; } #endif inline intptr_t** ContinuationHelper::Frame::callee_link_address(const frame& f) { - Unimplemented(); - return NULL; -} - -template -static inline intptr_t* real_fp(const frame& f) { - Unimplemented(); - return NULL; + return (intptr_t**)&f.own_abi()->callers_sp; } inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const frame& f) { - Unimplemented(); - return NULL; + return (address*)&f.callers_abi()->lr; } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { - Unimplemented(); +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { + intptr_t* sp = caller.unextended_sp(); + if (!f.is_heap_frame() && caller.is_interpreted_frame()) { + // See diagram "Interpreter Calling Procedure on PPC" at the end of continuationFreezeThaw_ppc.inline.hpp + sp = (intptr_t*)caller.at(ijava_idx(top_frame_sp)); + } + assert(f.is_interpreted_frame(), ""); + assert(f.is_heap_frame() || is_aligned(sp, frame::alignment_in_bytes), ""); + intptr_t* la = f.addr_at(ijava_idx(sender_sp)); + *la = f.is_heap_frame() ? (intptr_t)(sp - f.fp()) : (intptr_t)sp; } inline address* ContinuationHelper::Frame::return_pc_address(const frame& f) { - Unimplemented(); - return NULL; + return (address*)&f.callers_abi()->lr; } inline address ContinuationHelper::Frame::real_pc(const frame& f) { - Unimplemented(); - return NULL; + return (address)f.own_abi()->lr; } inline void ContinuationHelper::Frame::patch_pc(const frame& f, address pc) { - Unimplemented(); -} - + f.own_abi()->lr = (uint64_t)pc; +} + +// | Minimal ABI | +// | (frame::abi_minframe)| +// | 4 words | +// | Caller's SP |<- FP of f's caller +// |======================| +// | | Frame of f's caller +// | | +// frame_bottom of f ->| | +// |----------------------| +// | L0 aka P0 | +// | : | +// | : Pn | +// | : | +// | Lm | +// |----------------------| +// | SP alignment (opt.) | +// |----------------------| +// | Minimal ABI | +// | (frame::abi_minframe)| +// | 4 words | +// | Caller's SP |<- SP of f's caller / FP of f +// |======================| +// |ijava_state (metadata)| Frame of f +// | | +// | | +// |----------------------| +// | Expression stack | +// | | +// frame_top of f ->| | +// if callee interp. |......................| +// | L0 aka P0 |<- ijava_state.esp + callee_argsize +// | : | +// frame_top of f ->| : Pn | +// + metadata_words | : |<- ijava_state.esp (1 slot below Pn) +// if callee comp. | Lm | +// |----------------------| +// | SP alignment (opt.) | +// |----------------------| +// | Minimal ABI | +// | (frame::abi_minframe)| +// | 4 words | +// | Caller's SP |<- SP of f / FP of f's callee +// |======================| +// |ijava_state (metadata)| Frame of f's callee +// | | +// +// | Growth | +// v v +// +// See also diagram at the end of continuation_ppc.inline.hpp +// inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, InterpreterOopMap* mask) { // inclusive; this will be copied with the frame - Unimplemented(); - return NULL; + int expression_stack_sz = expression_stack_size(f, mask); + intptr_t* res = (intptr_t*)f.interpreter_frame_monitor_end() - expression_stack_sz; + assert(res <= (intptr_t*)f.get_ijava_state() - expression_stack_sz, + "res=" PTR_FORMAT " f.get_ijava_state()=" PTR_FORMAT " expression_stack_sz=%d", + p2i(res), p2i(f.get_ijava_state()), expression_stack_sz); + assert(res >= f.unextended_sp(), + "res: " INTPTR_FORMAT " ijava_state: " INTPTR_FORMAT " esp: " INTPTR_FORMAT " unextended_sp: " INTPTR_FORMAT " expression_stack_size: %d", + p2i(res), p2i(f.get_ijava_state()), f.get_ijava_state()->esp, p2i(f.unextended_sp()), expression_stack_sz); + return res; } -inline intptr_t* ContinuationHelper::InterpretedFrame::frame_bottom(const frame& f) { // exclusive; this will not be copied with the frame - Unimplemented(); - return NULL; +inline intptr_t* ContinuationHelper::InterpretedFrame::frame_bottom(const frame& f) { + return (intptr_t*)f.at(ijava_idx(locals)) + 1; // exclusive (will not be copied), so we add 1 word } -inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, int callee_argsize, bool callee_interpreted) { - Unimplemented(); - return NULL; +inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, int callee_argsize_incl_metadata, bool callee_interpreted) { + intptr_t* pseudo_unextended_sp = f.interpreter_frame_esp() + 1 - frame::metadata_words_at_top; + return pseudo_unextended_sp + (callee_interpreted ? callee_argsize_incl_metadata : 0); } #endif // CPU_PPC_CONTINUATIONFRAMEHELPERS_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index 0c049c2140155..43e4668644670 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -52,6 +52,9 @@ void RegisterMap::check_location_valid() { #endif // ASSERT bool frame::safe_for_sender(JavaThread *thread) { + if (is_heap_frame()) { + return true; + } address sp = (address)_sp; address fp = (address)_fp; address unextended_sp = (address)_unextended_sp; @@ -79,7 +82,7 @@ bool frame::safe_for_sender(JavaThread *thread) { // construct the sender and do some validation of it. This goes a long way // toward eliminating issues when we get in frame construction code - if (_cb != NULL ){ + if (_cb != NULL) { // First check if the frame is complete and the test is reliable. // Unfortunately we can only check frame completeness for runtime stubs @@ -118,6 +121,13 @@ bool frame::safe_for_sender(JavaThread *thread) { intptr_t* sender_sp = (intptr_t*) fp; address sender_pc = (address) sender_abi->lr;; + if (Continuation::is_return_barrier_entry(sender_pc)) { + // If our sender_pc is the return barrier, then our "real" sender is the continuation entry + frame s = Continuation::continuation_bottom_sender(thread, *this, sender_sp); + sender_sp = s.sp(); + sender_pc = s.pc(); + } + // We must always be able to find a recognizable pc. CodeBlob* sender_blob = CodeCache::find_blob(sender_pc); if (sender_blob == NULL) { @@ -175,10 +185,6 @@ bool frame::safe_for_sender(JavaThread *thread) { return true; } -bool frame::is_interpreted_frame() const { - return Interpreter::contains(pc()); -} - frame frame::sender_for_entry_frame(RegisterMap *map) const { assert(map != NULL, "map must be set"); // Java frame called from C; skip all C frames and return top C @@ -210,8 +216,23 @@ bool frame::upcall_stub_frame_is_first() const { } frame frame::sender_for_interpreter_frame(RegisterMap *map) const { - // Pass callers initial_caller_sp as unextended_sp. - return frame(sender_sp(), sender_pc(), (intptr_t*)get_ijava_state()->sender_sp); + // This is the sp before any possible extension (adapter/locals). + intptr_t* unextended_sp = interpreter_frame_sender_sp(); + address sender_pc = this->sender_pc(); + if (Continuation::is_return_barrier_entry(sender_pc)) { + if (map->walk_cont()) { // about to walk into an h-stack + return Continuation::top_frame(*this, map); + } else { + return Continuation::continuation_bottom_sender(map->thread(), *this, sender_sp()); + } + } + + return frame(sender_sp(), sender_pc, unextended_sp); +} + +intptr_t* frame::interpreter_frame_sender_sp() const { + assert(is_interpreted_frame(), "interpreted frame expected"); + return (intptr_t*)at(ijava_idx(sender_sp)); } void frame::patch_pc(Thread* thread, address pc) { @@ -379,6 +400,13 @@ void frame::describe_pd(FrameValues& values, int frame_no) { DESCRIBE_ADDRESS(lresult); DESCRIBE_ADDRESS(fresult); } + + if (is_java_frame() || Continuation::is_continuation_enterSpecial(*this)) { + intptr_t* ret_pc_loc = (intptr_t*)&own_abi()->lr; + address ret_pc = *(address*)ret_pc_loc; + values.describe(frame_no, ret_pc_loc, + Continuation::is_return_barrier_entry(ret_pc) ? "return address (return barrier)" : "return address"); + } } #endif @@ -395,7 +423,11 @@ frame::frame(void* sp, void* fp, void* pc) : frame((intptr_t*)sp, (address)pc) { // Pointer beyond the "oldest/deepest" BasicObjectLock on stack. BasicObjectLock* frame::interpreter_frame_monitor_end() const { - return (BasicObjectLock*) get_ijava_state()->monitors; + BasicObjectLock* result = (BasicObjectLock*) at(ijava_idx(monitors)); + // make sure the pointer points inside the frame + assert(sp() <= (intptr_t*) result, "monitor end should be above the stack pointer"); + assert((intptr_t*) result < fp(), "monitor end should be strictly below the frame pointer: result: " INTPTR_FORMAT " fp: " INTPTR_FORMAT, p2i(result), p2i(fp())); + return result; } intptr_t* frame::interpreter_frame_tos_at(jint offset) const { diff --git a/src/hotspot/cpu/ppc/frame_ppc.hpp b/src/hotspot/cpu/ppc/frame_ppc.hpp index ba4438d1b210b..e3c94ac06cea0 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.hpp @@ -268,9 +268,14 @@ ijava_state_size = sizeof(ijava_state) }; +// Byte offset relative to fp #define _ijava_state_neg(_component) \ (int) (-frame::ijava_state_size + offset_of(frame::ijava_state, _component)) +// Frame slot index relative to fp +#define ijava_idx(_component) \ + (_ijava_state_neg(_component) >> LogBytesPerWord) + // ENTRY_FRAME struct entry_frame_locals { @@ -356,15 +361,23 @@ // The frame's stack pointer before it has been extended by a c2i adapter; // needed by deoptimization - intptr_t* _unextended_sp; + union { + intptr_t* _unextended_sp; + int _offset_unextended_sp; // for use in stack-chunk frames + }; - // frame pointer for this frame - intptr_t* _fp; + union { + intptr_t* _fp; // frame pointer + int _offset_fp; // relative frame pointer for use in stack-chunk frames + }; public: // Accessors for fields - intptr_t* fp() const { return _fp; } + intptr_t* fp() const { assert_absolute(); return _fp; } + void set_fp(intptr_t* newfp) { _fp = newfp; } + int offset_fp() const { assert_offset(); return _offset_fp; } + void set_offset_fp(int value) { assert_on_heap(); _offset_fp = value; } // Accessors for ABIs inline abi_minframe* own_abi() const { return (abi_minframe*) _sp; } @@ -380,7 +393,10 @@ const ImmutableOopMap* get_oop_map() const; // Constructors + inline frame(intptr_t* sp, intptr_t* fp, address pc); inline frame(intptr_t* sp, address pc, intptr_t* unextended_sp = nullptr, intptr_t* fp = nullptr, CodeBlob* cb = nullptr); + inline frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map); + inline frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map, bool on_heap); private: address* sender_pc_addr(void) const; @@ -411,11 +427,22 @@ enum { // normal return address is 1 bundle past PC - pc_return_offset = 0, - metadata_words = 0, - frame_alignment = 16, + pc_return_offset = 0, + // size, in words, of frame metadata (e.g. pc and link) + metadata_words = sizeof(abi_minframe) >> LogBytesPerWord, + // size, in words, of metadata at frame bottom, i.e. it is not part of the + // caller/callee overlap + metadata_words_at_bottom = 0, + // size, in words, of frame metadata at the frame top, i.e. it is located + // between a callee frame and its stack arguments, where it is part + // of the caller/callee overlap + metadata_words_at_top = sizeof(abi_minframe) >> LogBytesPerWord, + // size, in words, of frame metadata at the frame top that needs + // to be reserved for callee functions in the runtime + frame_alignment = 16, + frame_alignment_in_words = frame_alignment >> LogBytesPerWord, // size, in words, of maximum shift in frame position due to alignment - align_wiggle = 1 + align_wiggle = 1 }; static jint interpreter_frame_expression_stack_direction() { return -1; } diff --git a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp index 37beacd9a62d2..27df0ce7ffe70 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp @@ -26,7 +26,8 @@ #ifndef CPU_PPC_FRAME_PPC_INLINE_HPP #define CPU_PPC_FRAME_PPC_INLINE_HPP -#include "code/codeCache.hpp" +#include "code/codeBlob.inline.hpp" +#include "code/codeCache.inline.hpp" #include "code/vmreg.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/align.hpp" @@ -44,17 +45,21 @@ inline void frame::setup() { _cb = CodeCache::find_blob(_pc); } - if (_fp == nullptr) { - _fp = (intptr_t*)own_abi()->callers_sp; - } - if (_unextended_sp == nullptr) { _unextended_sp = _sp; } - // When thawing continuation frames the _unextended_sp passed to the constructor is not aligend - assert(_on_heap || (is_aligned(_sp, alignment_in_bytes) && is_aligned(_fp, alignment_in_bytes)), - "invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp)); + if (_fp == nullptr) { + // The back link for compiled frames on the heap is not valid + if (is_heap_frame()) { + // fp for interpreted frames should have been derelativized and passed to the constructor + assert(is_compiled_frame(), ""); + // The back link for compiled frames on the heap is invalid. + _fp = _unextended_sp + _cb->frame_size(); + } else { + _fp = (intptr_t*)own_abi()->callers_sp; + } + } address original_pc = CompiledMethod::get_deopt_original_pc(this); if (original_pc != nullptr) { @@ -70,7 +75,10 @@ inline void frame::setup() { } } - assert(_on_heap || is_aligned(_sp, frame::frame_alignment), "SP must be 16-byte aligned"); + // Continuation frames on the java heap are not aligned. + // When thawing interpreted frames the sp can be unaligned (see new_stack_frame()). + assert(_on_heap || (is_aligned(_sp, alignment_in_bytes) || is_interpreted_frame()) && is_aligned(_fp, alignment_in_bytes), + "invalid alignment sp:" PTR_FORMAT " unextended_sp:" PTR_FORMAT " fp:" PTR_FORMAT, p2i(_sp), p2i(_unextended_sp), p2i(_fp)); } // Constructors @@ -79,13 +87,40 @@ inline void frame::setup() { inline frame::frame() : _sp(nullptr), _pc(nullptr), _cb(nullptr), _oop_map(nullptr), _deopt_state(unknown), _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(nullptr), _fp(nullptr) {} +inline frame::frame(intptr_t* sp) : frame(sp, nullptr) {} + +inline frame::frame(intptr_t* sp, intptr_t* fp, address pc) : frame(sp, pc, nullptr, fp, nullptr) {} + inline frame::frame(intptr_t* sp, address pc, intptr_t* unextended_sp, intptr_t* fp, CodeBlob* cb) : _sp(sp), _pc(pc), _cb(cb), _oop_map(nullptr), _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { setup(); } -inline frame::frame(intptr_t* sp) : frame(sp, nullptr) {} +inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, const ImmutableOopMap* oop_map) + : _sp(sp), _pc(pc), _cb(cb), _oop_map(oop_map), + _on_heap(false), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { + assert(_cb != nullptr, "pc: " INTPTR_FORMAT, p2i(pc)); + setup(); +} + +inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address pc, CodeBlob* cb, + const ImmutableOopMap* oop_map, bool on_heap) + : _sp(sp), _pc(pc), _cb(cb), _oop_map(oop_map), _deopt_state(not_deoptimized), + _on_heap(on_heap), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { + // In thaw, non-heap frames use this constructor to pass oop_map. I don't know why. + assert(_on_heap || _cb != nullptr, "these frames are always heap frames"); + if (cb != NULL) { + setup(); + } +#ifdef ASSERT + // The following assertion has been disabled because it would sometime trap for Continuation.run, + // which is not *in* a continuation and therefore does not clear the _cont_fastpath flag, but this + // is benign even in fast mode (see Freeze::setup_jump) + // We might freeze deoptimized frame in slow mode + // assert(_pc == pc && _deopt_state == not_deoptimized, ""); +#endif +} // Accessors @@ -111,11 +146,14 @@ inline int frame::frame_size() const { } // Return the frame's stack pointer before it has been extended by a -// c2i adapter. This is needed by deoptimization for ignoring c2i adapter -// frames. -inline intptr_t* frame::unextended_sp() const { - return _unextended_sp; -} +// c2i adapter. +// i2c adapters also modify the frame they are applied on but shared code +// must never use an interpreted frames unextended sp directly as the value +// is platform dependent. +inline intptr_t* frame::unextended_sp() const { assert_absolute(); return _unextended_sp; } +inline void frame::set_unextended_sp(intptr_t* value) { _unextended_sp = value; } +inline int frame::offset_unextended_sp() const { assert_offset(); return _offset_unextended_sp; } +inline void frame::set_offset_unextended_sp(int value) { assert_on_heap(); _offset_unextended_sp = value; } // All frames have this field. inline address frame::sender_pc() const { @@ -150,7 +188,7 @@ inline frame::ijava_state* frame::get_ijava_state() const { } inline intptr_t** frame::interpreter_frame_locals_addr() const { - return (intptr_t**) &(get_ijava_state()->locals); + return (intptr_t**)addr_at(ijava_idx(locals)); } inline intptr_t* frame::interpreter_frame_bcp_addr() const { @@ -183,7 +221,7 @@ inline oop* frame::interpreter_frame_temp_oop_addr() const { } inline intptr_t* frame::interpreter_frame_esp() const { - return (intptr_t*) get_ijava_state()->esp; + return (intptr_t*) at(ijava_idx(esp)); } // Convenient setters @@ -194,12 +232,13 @@ inline void frame::interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp) { inline void frame::interpreter_frame_set_sender_sp(intptr_t* sender_sp) { get_ijava_state()->sender_sp = (intptr_t) sender_sp; } inline intptr_t* frame::interpreter_frame_expression_stack() const { - return (intptr_t*)interpreter_frame_monitor_end() - 1; + intptr_t* monitor_end = (intptr_t*) interpreter_frame_monitor_end(); + return monitor_end-1; } // top of expression stack inline intptr_t* frame::interpreter_frame_tos_address() const { - return ((intptr_t*) get_ijava_state()->esp) + Interpreter::stackElementWords; + return (intptr_t*)at(ijava_idx(esp)) + Interpreter::stackElementWords; } inline int frame::interpreter_frame_monitor_size() { @@ -226,105 +265,121 @@ inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { return (JavaCallWrapper**)&get_entry_frame_locals()->call_wrapper_address; } -inline oop frame::saved_oop_result(RegisterMap* map) const { - return *((oop*)map->location(R3->as_VMReg(), nullptr)); +inline bool frame::is_interpreted_frame() const { + return Interpreter::contains(pc()); } -inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { - *((oop*)map->location(R3->as_VMReg(), nullptr)) = obj; -} +inline frame frame::sender_raw(RegisterMap* map) const { + // Default is we do have to follow them. The sender_for_xxx will + // update it accordingly. + map->set_include_argument_oops(false); -inline const ImmutableOopMap* frame::get_oop_map() const { - if (_cb == NULL) return NULL; - if (_cb->oop_maps() != NULL) { - NativePostCallNop* nop = nativePostCallNop_at(_pc); - if (nop != NULL && nop->displacement() != 0) { - int slot = ((nop->displacement() >> 24) & 0xff); - return _cb->oop_map_for_slot(slot, _pc); - } - const ImmutableOopMap* oop_map = OopMapSet::find_map(this); - return oop_map; + if (map->in_cont()) { // already in an h-stack + return map->stack_chunk()->sender(*this, map); } - return NULL; -} -inline int frame::compiled_frame_stack_argsize() const { - Unimplemented(); - return 0; -} + if (is_entry_frame()) return sender_for_entry_frame(map); + if (is_interpreted_frame()) return sender_for_interpreter_frame(map); -inline void frame::interpreted_frame_oop_map(InterpreterOopMap* mask) const { - Unimplemented(); -} - -inline int frame::sender_sp_ret_address_offset() { - Unimplemented(); - return 0; -} - -inline void frame::set_unextended_sp(intptr_t* value) { - Unimplemented(); -} - -inline int frame::offset_unextended_sp() const { - Unimplemented(); - return 0; -} + assert(_cb == CodeCache::find_blob(pc()), "Must be the same"); + if (_cb != nullptr) return sender_for_compiled_frame(map); -inline void frame::set_offset_unextended_sp(int value) { - Unimplemented(); + // Must be native-compiled frame, i.e. the marshaling code for native + // methods that exists in the core system. + return frame(sender_sp(), sender_pc()); } -//------------------------------------------------------------------------------ -// frame::sender - -frame frame::sender(RegisterMap* map) const { +inline frame frame::sender(RegisterMap* map) const { frame result = sender_raw(map); - if (map->process_frames()) { + if (map->process_frames() && !map->in_cont()) { StackWatermarkSet::on_iteration(map->thread(), result); } return result; } -inline frame frame::sender_raw(RegisterMap* map) const { - // Default is we do have to follow them. The sender_for_xxx will - // update it accordingly. - map->set_include_argument_oops(false); - - if (is_entry_frame()) return sender_for_entry_frame(map); - if (is_interpreted_frame()) return sender_for_interpreter_frame(map); - assert(_cb == CodeCache::find_blob(pc()),"Must be the same"); - - if (_cb != nullptr) return sender_for_compiled_frame(map); - - // Must be native-compiled frame, i.e. the marshaling code for native - // methods that exists in the core system. - return frame(sender_sp(), sender_pc()); -} - inline frame frame::sender_for_compiled_frame(RegisterMap *map) const { assert(map != nullptr, "map must be set"); intptr_t* sender_sp = this->sender_sp(); address sender_pc = this->sender_pc(); - // Now adjust the map. if (map->update_map()) { // Tell GC to use argument oopmaps for some runtime stubs that need it. - map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); - if (_cb->oop_maps() != nullptr) { - OopMapSet::update_register_map(this, map); + // For C1, the runtime stub might not have oop maps, so set this flag + // outside of update_register_map. + if (!_cb->is_compiled()) { // compiled frames do not use callee-saved registers + map->set_include_argument_oops(_cb->caller_must_gc_arguments(map->thread())); + if (oop_map() != nullptr) { + _oop_map->update_register_map(this, map); + } + } else { + assert(!_cb->caller_must_gc_arguments(map->thread()), ""); + assert(!map->include_argument_oops(), ""); + assert(oop_map() == NULL || !oop_map()->has_any(OopMapValue::callee_saved_value), "callee-saved value in compiled frame"); + } + } + + assert(sender_sp != sp(), "must have changed"); + + if (Continuation::is_return_barrier_entry(sender_pc)) { + if (map->walk_cont()) { // about to walk into an h-stack + return Continuation::top_frame(*this, map); + } else { + return Continuation::continuation_bottom_sender(map->thread(), *this, sender_sp); } } return frame(sender_sp, sender_pc); } +inline oop frame::saved_oop_result(RegisterMap* map) const { + oop* result_adr = (oop *)map->location(R3->as_VMReg(), sp()); + guarantee(result_adr != NULL, "bad register save location"); + return *result_adr; +} + +inline void frame::set_saved_oop_result(RegisterMap* map, oop obj) { + oop* result_adr = (oop *)map->location(R3->as_VMReg(), sp()); + guarantee(result_adr != NULL, "bad register save location"); + + *result_adr = obj; +} + +inline const ImmutableOopMap* frame::get_oop_map() const { + if (_cb == NULL) return NULL; + if (_cb->oop_maps() != NULL) { + NativePostCallNop* nop = nativePostCallNop_at(_pc); + if (nop != NULL && nop->displacement() != 0) { + int slot = ((nop->displacement() >> 24) & 0xff); + return _cb->oop_map_for_slot(slot, _pc); + } + const ImmutableOopMap* oop_map = OopMapSet::find_map(this); + return oop_map; + } + return NULL; +} + +inline int frame::compiled_frame_stack_argsize() const { + assert(cb()->is_compiled(), ""); + return (cb()->as_compiled_method()->method()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord; +} + +inline void frame::interpreted_frame_oop_map(InterpreterOopMap* mask) const { + assert(mask != NULL, ""); + Method* m = interpreter_frame_method(); + int bci = interpreter_frame_bci(); + m->mask_for(bci, mask); // OopMapCache::compute_one_oop_map(m, bci, mask); +} + +inline int frame::sender_sp_ret_address_offset() { + return -(int)(_abi0(lr) >> LogBytesPerWord); // offset in words +} + template void frame::update_map_with_saved_link(RegisterMapT* map, intptr_t** link_addr) { - Unimplemented(); + // Nothing to do. } #endif // CPU_PPC_FRAME_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp index e1de586e91a87..c14d05d284a65 100644 --- a/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp +++ b/src/hotspot/cpu/ppc/globalDefinitions_ppc.hpp @@ -37,6 +37,8 @@ const bool CCallingConventionRequiresIntsAsLongs = true; #define SUPPORTS_NATIVE_CX8 +#define SUPPORT_MONITOR_COUNT + // PPC64 is not specified as multi-copy-atomic // So we must not #define CPU_MULTI_COPY_ATOMIC diff --git a/src/hotspot/cpu/ppc/globals_ppc.hpp b/src/hotspot/cpu/ppc/globals_ppc.hpp index ebf03908fe8c7..44b321635e1c7 100644 --- a/src/hotspot/cpu/ppc/globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/globals_ppc.hpp @@ -54,7 +54,7 @@ define_pd_global(intx, StackRedPages, DEFAULT_STACK_RED_PAGES); define_pd_global(intx, StackShadowPages, DEFAULT_STACK_SHADOW_PAGES); define_pd_global(intx, StackReservedPages, DEFAULT_STACK_RESERVED_PAGES); -define_pd_global(bool, VMContinuations, false); +define_pd_global(bool, VMContinuations, true); // Use large code-entry alignment. define_pd_global(uintx, CodeCacheSegmentSize, 128); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp index f289b80815033..d4afc68d8f431 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc.hpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc.hpp @@ -199,7 +199,7 @@ class InterpreterMacroAssembler: public MacroAssembler { void static_dload_or_store(int which_local, LoadOrStore direction); void save_interpreter_state(Register scratch); - void restore_interpreter_state(Register scratch, bool bcp_and_mdx_only = false); + void restore_interpreter_state(Register scratch, bool bcp_and_mdx_only = false, bool restore_top_frame_sp = false); void increment_backedge_counter(const Register Rcounters, Register Rtmp, Register Rtmp2, Register Rscratch); diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index 5b0a8026528f6..1e0ef653c3b9a 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -894,6 +894,7 @@ void InterpreterMacroAssembler::remove_activation(TosState state, merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2); mtlr(R0); + pop_cont_fastpath(); BLOCK_COMMENT("} remove_activation"); } @@ -927,7 +928,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { const Register current_header = R9_ARG7; const Register tmp = R10_ARG8; - Label done; + Label count_locking, done; Label cas_failed, slow_case; assert_different_registers(displaced_header, object_mark_addr, current_header, tmp); @@ -972,7 +973,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // If the compare-and-exchange succeeded, then we found an unlocked // object and we have now locked it. - b(done); + b(count_locking); bind(cas_failed); // } else if (THREAD->is_lock_owned((address)displaced_header)) @@ -994,7 +995,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { bne(CCR0, slow_case); std(R0/*==0!*/, BasicObjectLock::lock_offset_in_bytes() + BasicLock::displaced_header_offset_in_bytes(), monitor); - b(done); + b(count_locking); // } else { // // Slow path. @@ -1004,8 +1005,11 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) { // slow case of monitor enter. bind(slow_case); call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor); + b(done); // } align(32, 12); + bind(count_locking); + inc_held_monitor_count(current_header /*tmp*/); bind(done); } } @@ -1095,6 +1099,7 @@ void InterpreterMacroAssembler::unlock_object(Register monitor) { bind(free_slot); li(R0, 0); std(R0, BasicObjectLock::obj_offset_in_bytes(), monitor); + dec_held_monitor_count(current_header /*tmp*/); bind(done); } } @@ -2164,8 +2169,17 @@ void InterpreterMacroAssembler::save_interpreter_state(Register scratch) { // Other entries should be unchanged. } -void InterpreterMacroAssembler::restore_interpreter_state(Register scratch, bool bcp_and_mdx_only) { +void InterpreterMacroAssembler::restore_interpreter_state(Register scratch, bool bcp_and_mdx_only, bool restore_top_frame_sp) { ld(scratch, 0, R1_SP); + if (restore_top_frame_sp) { + // After thawing the top frame of a continuation we reach here with frame::abi_minframe. + // therefore we have to restore top_frame_sp before the assertion below. + assert(!bcp_and_mdx_only, "chose other registers"); + Register tfsp = R18_locals; + Register scratch2 = R26_monitor; + ld(tfsp, _ijava_state_neg(top_frame_sp), scratch); + resize_frame_absolute(tfsp, scratch2, R0); + } ld(R14_bcp, _ijava_state_neg(bcp), scratch); // Changed by VM code (exception). if (ProfileInterpreter) { ld(R28_mdx, _ijava_state_neg(mdx), scratch); } // Changed by VM code. if (!bcp_and_mdx_only) { diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp index 60c2f190bfea1..49166a2e1ade4 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp @@ -1180,6 +1180,14 @@ address MacroAssembler::call_c_using_toc(const FunctionDescriptor* fd, } #endif // ABI_ELFv2 +void MacroAssembler::post_call_nop() { + // Make inline again when loom is always enabled. + if (!Continuations::enabled()) { + return; + } + nop(); +} + void MacroAssembler::call_VM_base(Register oop_result, Register last_java_sp, address entry_point, @@ -2624,6 +2632,7 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register Label cont; Label object_has_monitor; Label cas_failed; + Label success, failure; // Load markWord from object into displaced_header. ld(displaced_header, oopDesc::mark_offset_in_bytes(), oop); @@ -2632,7 +2641,7 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register load_klass(temp, oop); lwz(temp, in_bytes(Klass::access_flags_offset()), temp); testbitdi(flag, R0, temp, exact_log2(JVM_ACC_IS_VALUE_BASED_CLASS)); - bne(flag, cont); + bne(flag, failure); } #if INCLUDE_RTM_OPT @@ -2670,15 +2679,15 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register &cas_failed, /*check without membar and ldarx first*/true); assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + // If the compare-and-exchange succeeded, then we found an unlocked + // object and we have now locked it. + b(success); } else { // Set NE to indicate 'failure' -> take slow-path. crandc(flag, Assembler::equal, flag, Assembler::equal); + b(failure); } - // If the compare-and-exchange succeeded, then we found an unlocked - // object and we have now locked it. - b(cont); - bind(cas_failed); // We did not see an unlocked object so try the fast recursive case. @@ -2693,9 +2702,9 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register mcrf(flag,CCR0); std(R0/*==0, perhaps*/, BasicLock::displaced_header_offset_in_bytes(), box); - // Handle existing monitor. b(cont); + // Handle existing monitor. bind(object_has_monitor); // The object's monitor m is unlocked iff m->owner == NULL, // otherwise m->owner may contain a thread or a stack address. @@ -2720,11 +2729,11 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register // Store a non-null value into the box. std(box, BasicLock::displaced_header_offset_in_bytes(), box); - beq(flag, cont); + beq(flag, success); // Check for recursive locking. cmpd(flag, current_header, R16_thread); - bne(flag, cont); + bne(flag, failure); // Current thread already owns the lock. Just increment recursions. Register recursions = displaced_header; @@ -2737,8 +2746,12 @@ void MacroAssembler::compiler_fast_lock_object(ConditionRegister flag, Register #endif bind(cont); - // flag == EQ indicates success + // flag == EQ indicates success, increment held monitor count // flag == NE indicates failure + bne(flag, failure); + bind(success); + inc_held_monitor_count(temp); + bind(failure); } void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Register oop, Register box, @@ -2746,7 +2759,8 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe bool use_rtm) { assert_different_registers(oop, box, temp, displaced_header, current_header); assert(flag != CCR0, "bad condition register"); - Label cont, object_has_monitor, notRecursive; + Label object_has_monitor, notRecursive; + Label success, failure; #if INCLUDE_RTM_OPT if (UseRTMForStackLocks && use_rtm) { @@ -2756,7 +2770,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe cmpwi(flag, R0, markWord::unlocked_value); // bits = 01 unlocked bne(flag, L_regular_unlock); // else RegularLock tend_(); // otherwise end... - b(cont); // ... and we're done + b(success); // ... and we're done bind(L_regular_unlock); } #endif @@ -2767,7 +2781,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe // If the displaced header is 0, we have a recursive unlock. cmpdi(flag, displaced_header, 0); - beq(flag, cont); + beq(flag, success); } // Handle existing monitor. @@ -2789,16 +2803,16 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe MacroAssembler::MemBarRel, MacroAssembler::cmpxchgx_hint_release_lock(), noreg, - &cont); + &failure); assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0"); + b(success); } else { // Set NE to indicate 'failure' -> take slow-path. crandc(flag, Assembler::equal, flag, Assembler::equal); + b(failure); } // Handle existing monitor. - b(cont); - bind(object_has_monitor); STATIC_ASSERT(markWord::monitor_value <= INT_MAX); addi(current_header, current_header, -(int)markWord::monitor_value); // monitor @@ -2812,7 +2826,7 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe cmpdi(flag, temp, 0); bne(flag, L_regular_inflated_unlock); tend_(); - b(cont); + b(success); bind(L_regular_inflated_unlock); } #endif @@ -2820,25 +2834,27 @@ void MacroAssembler::compiler_fast_unlock_object(ConditionRegister flag, Registe ld(displaced_header, ObjectMonitor::recursions_offset_in_bytes(), current_header); cmpd(flag, temp, R16_thread); - bne(flag, cont); + bne(flag, failure); addic_(displaced_header, displaced_header, -1); blt(CCR0, notRecursive); // Not recursive if negative after decrement. std(displaced_header, ObjectMonitor::recursions_offset_in_bytes(), current_header); - b(cont); // flag is already EQ here. + b(success); // flag is already EQ here. bind(notRecursive); ld(temp, ObjectMonitor::EntryList_offset_in_bytes(), current_header); ld(displaced_header, ObjectMonitor::cxq_offset_in_bytes(), current_header); orr(temp, temp, displaced_header); // Will be 0 if both are 0. cmpdi(flag, temp, 0); - bne(flag, cont); + bne(flag, failure); release(); std(temp, ObjectMonitor::owner_offset_in_bytes(), current_header); - bind(cont); - // flag == EQ indicates success + // flag == EQ indicates success, decrement held monitor count // flag == NE indicates failure + bind(success); + dec_held_monitor_count(temp); + bind(failure); } void MacroAssembler::safepoint_poll(Label& slow_path, Register temp, bool at_return, bool in_nmethod) { @@ -4370,3 +4386,48 @@ void MacroAssembler::cache_wbsync(bool is_presync) { fence(); } } + +void MacroAssembler::push_cont_fastpath() { + Label done; + ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); + cmpld(CCR0, R1_SP, R0); + ble(CCR0, done); + st_ptr(R1_SP, JavaThread::cont_fastpath_offset(), R16_thread); + bind(done); +} + +void MacroAssembler::pop_cont_fastpath() { + Label done; + ld_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); + cmpld(CCR0, R1_SP, R0); + ble(CCR0, done); + li(R0, 0); + st_ptr(R0, JavaThread::cont_fastpath_offset(), R16_thread); + bind(done); +} + +void MacroAssembler::inc_held_monitor_count(Register tmp) { + ld(tmp, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); +#ifdef ASSERT + Label ok; + cmpdi(CCR0, tmp, 0); + bge_predict_taken(CCR0, ok); + stop("held monitor count is negativ at increment"); + bind(ok); +#endif + addi(tmp, tmp, 1); + std(tmp, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); +} + +void MacroAssembler::dec_held_monitor_count(Register tmp) { + ld(tmp, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); +#ifdef ASSERT + Label ok; + cmpdi(CCR0, tmp, 0); + bgt_predict_taken(CCR0, ok); + stop("held monitor count is <= 0 at decrement"); + bind(ok); +#endif + addi(tmp, tmp, -1); + std(tmp, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); +} diff --git a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp index 55318aaa59c1d..9c0269cb259e6 100644 --- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp @@ -34,6 +34,7 @@ // MacroAssembler extends Assembler by a few frequently used macros. class ciTypeArray; +class OopMap; class MacroAssembler: public Assembler { public: @@ -416,6 +417,8 @@ class MacroAssembler: public Assembler { inline address call_stub(Register function_entry); inline void call_stub_and_return_to(Register function_entry, Register return_pc); + void post_call_nop(); + // // Java utilities // @@ -599,6 +602,11 @@ class MacroAssembler: public Assembler { // Method handle support (JSR 292). RegisterOrConstant argument_offset(RegisterOrConstant arg_slot, Register temp_reg, int extra_slot_offset = 0); + void push_cont_fastpath(); + void pop_cont_fastpath(); + void inc_held_monitor_count(Register tmp); + void dec_held_monitor_count(Register tmp); + // allocation (for C1) void tlab_allocate( Register obj, // result: pointer to object after successful allocation diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp index 63684b2db43eb..19ad9dab518c0 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp @@ -43,7 +43,7 @@ // We use an illtrap for marking a method as not_entrant // Work around a C++ compiler bug which changes 'this' bool NativeInstruction::is_sigill_not_entrant_at(address addr) { - if (*(int*)addr != 0 /*illtrap*/) return false; + if (!Assembler::is_illtrap(addr)) return false; CodeBlob* cb = CodeCache::find_blob(addr); if (cb == NULL || !cb->is_nmethod()) return false; nmethod *nm = (nmethod *)cb; @@ -424,3 +424,33 @@ void NativeCallTrampolineStub::set_destination(address new_destination) { *(address*)(ctable + destination_toc_offset()) = new_destination; } + +void NativePostCallNop::make_deopt() { + NativeDeoptInstruction::insert(addr_at(0)); +} + +void NativePostCallNop::patch(jint diff) { + // unsupported for now +} + +void NativeDeoptInstruction::verify() { +} + +bool NativeDeoptInstruction::is_deopt_at(address code_pos) { + if (!Assembler::is_illtrap(code_pos)) return false; + CodeBlob* cb = CodeCache::find_blob(code_pos); + if (cb == NULL || !cb->is_compiled()) return false; + nmethod *nm = (nmethod *)cb; + // see NativeInstruction::is_sigill_not_entrant_at() + return nm->verified_entry_point() != code_pos; +} + +// Inserts an undefined instruction at a given pc +void NativeDeoptInstruction::insert(address code_pos) { + ResourceMark rm; + int code_size = 1 * BytesPerInstWord; + CodeBuffer cb(code_pos, code_size + 1); + MacroAssembler* a = new MacroAssembler(&cb); + a->illtrap(); + ICache::ppc64_flush_icache_bytes(code_pos, code_size); +} diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.hpp b/src/hotspot/cpu/ppc/nativeInst_ppc.hpp index 7d6e6cff5a5c8..507de109d6063 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.hpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.hpp @@ -51,7 +51,9 @@ class NativeInstruction { friend class Relocation; public: - bool is_jump() { return Assembler::is_b(long_at(0)); } // See NativeGeneralJump. + bool is_nop() const { return Assembler::is_nop(long_at(0)); } + + bool is_jump() const { return Assembler::is_b(long_at(0)); } // See NativeGeneralJump. bool is_sigtrap_ic_miss_check() { assert(UseSIGTRAP, "precondition"); @@ -505,33 +507,36 @@ class NativeMovRegMem: public NativeInstruction { class NativePostCallNop: public NativeInstruction { public: - bool check() const { Unimplemented(); return false; } + bool check() const { return is_nop(); } int displacement() const { return 0; } - void patch(jint diff) { Unimplemented(); } - void make_deopt() { Unimplemented(); } + void patch(jint diff); + void make_deopt(); }; inline NativePostCallNop* nativePostCallNop_at(address address) { - // Unimplemented(); + NativePostCallNop* nop = (NativePostCallNop*) address; + if (nop->check()) { + return nop; + } return NULL; } class NativeDeoptInstruction: public NativeInstruction { -public: - address instruction_address() const { Unimplemented(); return NULL; } - address next_instruction_address() const { Unimplemented(); return NULL; } + public: + enum { + instruction_size = 4, + instruction_offset = 0, + }; - void verify() { Unimplemented(); } + address instruction_address() const { return addr_at(instruction_offset); } + address next_instruction_address() const { return addr_at(instruction_size); } - static bool is_deopt_at(address instr) { - // Unimplemented(); - return false; - } + void verify(); + + static bool is_deopt_at(address code_pos); // MT-safe patching - static void insert(address code_pos) { - Unimplemented(); - } + static void insert(address code_pos); }; #endif // CPU_PPC_NATIVEINST_PPC_HPP diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 6488b5ffe9b07..4a6f36136605f 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3447,6 +3447,7 @@ encode %{ return; } } + __ post_call_nop(); %} // Second node of expanded dynamic call - the call. @@ -3487,6 +3488,7 @@ encode %{ // and the entry point might be too far away for bl. Pc() serves // as dummy and bl will be patched later. __ bl((address) __ pc()); + __ post_call_nop(); %} // postalloc expand emitter for virtual calls. @@ -3601,6 +3603,7 @@ encode %{ assert(((MachCallDynamicJavaNode*)this)->ret_addr_offset() == __ offset() - start_offset, "Fix constant in ret_addr_offset(), expected %d", __ offset() - start_offset); } + __ post_call_nop(); %} // a runtime call @@ -3612,6 +3615,7 @@ encode %{ #if defined(ABI_ELFv2) address entry= !($meth$$method) ? NULL : (address)$meth$$method; __ call_c(entry, relocInfo::runtime_call_type); + __ post_call_nop(); #else // The function we're going to call. FunctionDescriptor fdtemp; @@ -3627,6 +3631,7 @@ encode %{ ciEnv::current()->record_out_of_memory_failure(); return; } + __ post_call_nop(); #endif // Check the ret_addr_offset. @@ -14378,7 +14383,7 @@ instruct CallStaticJavaDirect(method meth) %{ ins_num_consts(3 /* up to 3 patchable constants: inline cache, 2 call targets. */); format %{ "CALL,static $meth \t// ==> " %} - size(4); + size(8); ins_encode( enc_java_static_call(meth) ); ins_pipe(pipe_class_call); %} @@ -14399,7 +14404,7 @@ instruct CallDynamicJavaDirectSched(method meth) %{ ins_num_consts(1 /* 1 patchable constant: call destination */); format %{ "BL \t// dynamic $meth ==> " %} - size(4); + size(8); ins_encode( enc_java_dynamic_call_sched(meth) ); ins_pipe(pipe_class_call); %} @@ -14477,9 +14482,10 @@ instruct CallLeafDirect(method meth) %{ predicate(false); // but never match. format %{ "BCTRL \t// leaf call $meth ==> " %} - size(4); + size(8); ins_encode %{ __ bctrl(); + __ post_call_nop(); %} ins_pipe(pipe_class_call); %} diff --git a/src/hotspot/cpu/ppc/register_ppc.hpp b/src/hotspot/cpu/ppc/register_ppc.hpp index cf91cbbc16ecd..3d462a018f987 100644 --- a/src/hotspot/cpu/ppc/register_ppc.hpp +++ b/src/hotspot/cpu/ppc/register_ppc.hpp @@ -752,10 +752,10 @@ REGISTER_DECLARATION(FloatRegister, F13_ARG13, F13); // volatile // Register declarations to be used in frame manager assembly code. // Use only non-volatile registers in order to keep values across C-calls. REGISTER_DECLARATION(Register, R14_bcp, R14); -REGISTER_DECLARATION(Register, R15_esp, R15); +REGISTER_DECLARATION(Register, R15_esp, R15); // slot below top of expression stack for ld/st with update REGISTER_DECLARATION(FloatRegister, F15_ftos, F15); REGISTER_DECLARATION(Register, R16_thread, R16); // address of current thread -REGISTER_DECLARATION(Register, R17_tos, R17); // address of Java tos (prepushed). +REGISTER_DECLARATION(Register, R17_tos, R17); // The interpreter's top of (expression) stack cache register REGISTER_DECLARATION(Register, R18_locals, R18); // address of first param slot (receiver). REGISTER_DECLARATION(Register, R19_method, R19); // address of current method #ifndef DONT_USE_REGISTER_DEFINES diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 0902c0f35ed64..aeb9b9dfff99f 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.inline.hpp" #include "code/debugInfoRec.hpp" +#include "code/compiledIC.hpp" #include "code/icBuffer.hpp" #include "code/vtableStubs.hpp" #include "frame_ppc.hpp" @@ -37,6 +38,8 @@ #include "oops/compiledICHolder.hpp" #include "oops/klass.inline.hpp" #include "prims/methodHandles.hpp" +#include "runtime/continuation.hpp" +#include "runtime/continuationEntry.inline.hpp" #include "runtime/jniHandles.hpp" #include "runtime/os.inline.hpp" #include "runtime/safepointMechanism.hpp" @@ -626,6 +629,9 @@ const VMReg java_farg_reg[13] = { const int num_java_iarg_registers = sizeof(java_iarg_reg) / sizeof(java_iarg_reg[0]); const int num_java_farg_registers = sizeof(java_farg_reg) / sizeof(java_farg_reg[0]); +STATIC_ASSERT(num_java_iarg_registers == Argument::n_int_register_parameters_j); +STATIC_ASSERT(num_java_farg_registers == Argument::n_float_register_parameters_j); + int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed) { @@ -1174,6 +1180,8 @@ void SharedRuntime::gen_i2c_adapter(MacroAssembler *masm, } } + __ push_cont_fastpath(); // Set JavaThread::_cont_fastpath to the sp of the oldest interpreted frame we know about + BLOCK_COMMENT("Store method"); // Store method into thread->callee_target. // We might end up in handle_wrong_method if the callee is @@ -1618,6 +1626,378 @@ static void gen_special_dispatch(MacroAssembler* masm, receiver_reg, member_reg, /*for_compiler_entry:*/ true); } +//---------------------------- continuation_enter_setup --------------------------- +// +// Frame setup. +// +// Arguments: +// None. +// +// Results: +// R1_SP: pointer to blank ContinuationEntry in the pushed frame. +// +// Kills: +// R0, R20 +// +static OopMap* continuation_enter_setup(MacroAssembler* masm, int& framesize_words) { + assert(ContinuationEntry::size() % VMRegImpl::stack_slot_size == 0, ""); + assert(in_bytes(ContinuationEntry::cont_offset()) % VMRegImpl::stack_slot_size == 0, ""); + assert(in_bytes(ContinuationEntry::chunk_offset()) % VMRegImpl::stack_slot_size == 0, ""); + + const int frame_size_in_bytes = (int)ContinuationEntry::size(); + assert(is_aligned(frame_size_in_bytes, frame::alignment_in_bytes), "alignment error"); + + framesize_words = frame_size_in_bytes / wordSize; + + DEBUG_ONLY(__ block_comment("setup {")); + // Save return pc and push entry frame + const Register return_pc = R20; + __ mflr(return_pc); + __ std(return_pc, _abi0(lr), R1_SP); // SP->lr = return_pc + __ push_frame(frame_size_in_bytes , R0); // SP -= frame_size_in_bytes + + OopMap* map = new OopMap((int)frame_size_in_bytes / VMRegImpl::stack_slot_size, 0 /* arg_slots*/); + ContinuationEntry::setup_oopmap(map); + + __ ld_ptr(R0, JavaThread::cont_entry_offset(), R16_thread); + __ st_ptr(R1_SP, JavaThread::cont_entry_offset(), R16_thread); + __ st_ptr(R0, ContinuationEntry::parent_offset(), R1_SP); + DEBUG_ONLY(__ block_comment("} setup")); + + return map; +} + +//---------------------------- fill_continuation_entry --------------------------- +// +// Initialize the new ContinuationEntry. +// +// Arguments: +// R1_SP: pointer to blank Continuation entry +// reg_cont_obj: pointer to the continuation +// reg_flags: flags +// +// Results: +// R1_SP: pointer to filled out ContinuationEntry +// +// Kills: +// R8_ARG6, R9_ARG7, R10_ARG8 +// +static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, Register reg_flags) { + assert_different_registers(reg_cont_obj, reg_flags); + Register zero = R8_ARG6; + Register tmp2 = R9_ARG7; + Register tmp3 = R10_ARG8; + + DEBUG_ONLY(__ block_comment("fill {")); +#ifdef ASSERT + __ load_const_optimized(tmp2, 0x1234); + __ stw(tmp2, in_bytes(ContinuationEntry::cookie_offset()), R1_SP); +#endif //ASSERT + + __ li(zero, 0); + __ st_ptr(reg_cont_obj, ContinuationEntry::cont_offset(), R1_SP); + __ stw(reg_flags, in_bytes(ContinuationEntry::flags_offset()), R1_SP); + __ st_ptr(zero, ContinuationEntry::chunk_offset(), R1_SP); + __ stw(zero, in_bytes(ContinuationEntry::argsize_offset()), R1_SP); + __ stw(zero, in_bytes(ContinuationEntry::pin_count_offset()), R1_SP); + + __ ld_ptr(tmp2, JavaThread::cont_fastpath_offset(), R16_thread); + __ ld(tmp3, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); + __ st_ptr(tmp2, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP); + __ std(tmp3, in_bytes(ContinuationEntry::parent_held_monitor_count_offset()), R1_SP); + + __ st_ptr(zero, JavaThread::cont_fastpath_offset(), R16_thread); + __ std(zero, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); + DEBUG_ONLY(__ block_comment("} fill")); +} + +//---------------------------- continuation_enter_cleanup --------------------------- +// +// Copy corresponding attributes from the top ContinuationEntry to the JavaThread +// before deleting it. +// +// Arguments: +// R1_SP: pointer to the ContinuationEntry +// +// Results: +// None. +// +// Kills: +// R8_ARG6, R9_ARG7, R10_ARG8 +// +static void continuation_enter_cleanup(MacroAssembler* masm) { + Register tmp1 = R8_ARG6; + Register tmp2 = R9_ARG7; + Register tmp3 = R10_ARG8; + +#ifdef ASSERT + __ block_comment("clean {"); + __ ld_ptr(tmp1, JavaThread::cont_entry_offset(), R16_thread); + __ cmpd(CCR0, R1_SP, tmp1); + __ asm_assert_eq(FILE_AND_LINE ": incorrect R1_SP"); +#endif + + __ ld_ptr(tmp1, ContinuationEntry::parent_cont_fastpath_offset(), R1_SP); + __ ld(tmp2, in_bytes(ContinuationEntry::parent_held_monitor_count_offset()), R1_SP); + __ ld_ptr(tmp3, ContinuationEntry::parent_offset(), R1_SP); + __ st_ptr(tmp1, JavaThread::cont_fastpath_offset(), R16_thread); + __ std(tmp2, in_bytes(JavaThread::held_monitor_count_offset()), R16_thread); + __ st_ptr(tmp3, JavaThread::cont_entry_offset(), R16_thread); + DEBUG_ONLY(__ block_comment("} clean")); +} + +static void check_continuation_enter_argument(VMReg actual_vmreg, + Register expected_reg, + const char* name) { + assert(!actual_vmreg->is_stack(), "%s cannot be on stack", name); + assert(actual_vmreg->as_Register() == expected_reg, + "%s is in unexpected register: %s instead of %s", + name, actual_vmreg->as_Register()->name(), expected_reg->name()); +} + +static void gen_continuation_enter(MacroAssembler* masm, + const VMRegPair* regs, + int& exception_offset, + OopMapSet* oop_maps, + int& frame_complete, + int& framesize_words, + int& interpreted_entry_offset, + int& compiled_entry_offset) { + + // enterSpecial(Continuation c, boolean isContinue, boolean isVirtualThread) + int pos_cont_obj = 0; + int pos_is_cont = 1; + int pos_is_virtual = 2; + + // The platform-specific calling convention may present the arguments in various registers. + // To simplify the rest of the code, we expect the arguments to reside at these known + // registers, and we additionally check the placement here in case calling convention ever + // changes. + Register reg_cont_obj = R3_ARG1; + Register reg_is_cont = R4_ARG2; + Register reg_is_virtual = R5_ARG3; + + check_continuation_enter_argument(regs[pos_cont_obj].first(), reg_cont_obj, "Continuation object"); + check_continuation_enter_argument(regs[pos_is_cont].first(), reg_is_cont, "isContinue"); + check_continuation_enter_argument(regs[pos_is_virtual].first(), reg_is_virtual, "isVirtualThread"); + + address resolve_static_call = SharedRuntime::get_resolve_static_call_stub(); + + address start = __ pc(); + + Label L_thaw, L_exit; + + // i2i entry used at interp_only_mode only + interpreted_entry_offset = __ pc() - start; + { +#ifdef ASSERT + Label is_interp_only; + __ lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread); + __ cmpwi(CCR0, R0, 0); + __ bne(CCR0, is_interp_only); + __ stop("enterSpecial interpreter entry called when not in interp_only_mode"); + __ bind(is_interp_only); +#endif + + // Read interpreter arguments into registers (this is an ad-hoc i2c adapter) + __ ld(reg_cont_obj, Interpreter::stackElementSize*3, R15_esp); + __ lwz(reg_is_cont, Interpreter::stackElementSize*2, R15_esp); + __ lwz(reg_is_virtual, Interpreter::stackElementSize*1, R15_esp); + + __ push_cont_fastpath(); + + OopMap* map = continuation_enter_setup(masm, framesize_words); + + // The frame is complete here, but we only record it for the compiled entry, so the frame would appear unsafe, + // but that's okay because at the very worst we'll miss an async sample, but we're in interp_only_mode anyway. + + fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); + + // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) + __ cmpwi(CCR0, reg_is_cont, 0); + __ bne(CCR0, L_thaw); + + // --- call Continuation.enter(Continuation c, boolean isContinue) + + // Emit compiled static call. The call will be always resolved to the c2i + // entry of Continuation.enter(Continuation c, boolean isContinue). + // There are special cases in SharedRuntime::resolve_static_call_C() and + // SharedRuntime::resolve_sub_helper_internal() to achieve this + // See also corresponding call below. + address c2i_call_pc = __ pc(); + int start_offset = __ offset(); + // Put the entry point as a constant into the constant pool. + const address entry_point_toc_addr = __ address_constant(resolve_static_call, RelocationHolder::none); + const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr); + guarantee(entry_point_toc_addr != nullptr, "const section overflow"); + + // Emit the trampoline stub which will be related to the branch-and-link below. + address stub = __ emit_trampoline_stub(entry_point_toc_offset, start_offset); + guarantee(stub != nullptr, "no space for trampoline stub"); + + __ relocate(relocInfo::static_call_type); + // Note: At this point we do not have the address of the trampoline + // stub, and the entry point might be too far away for bl, so __ pc() + // serves as dummy and the bl will be patched later. + __ bl(__ pc()); + oop_maps->add_gc_map(__ pc() - start, map); + __ post_call_nop(); + + __ b(L_exit); + + // static stub for the call above + CodeBuffer* cbuf = masm->code_section()->outer(); + stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, c2i_call_pc); + guarantee(stub != nullptr, "no space for static stub"); + } + + // compiled entry + __ align(CodeEntryAlignment); + compiled_entry_offset = __ pc() - start; + + OopMap* map = continuation_enter_setup(masm, framesize_words); + + // Frame is now completed as far as size and linkage. + frame_complete =__ pc() - start; + + fill_continuation_entry(masm, reg_cont_obj, reg_is_virtual); + + // If isContinue, call to thaw. Otherwise, call Continuation.enter(Continuation c, boolean isContinue) + __ cmpwi(CCR0, reg_is_cont, 0); + __ bne(CCR0, L_thaw); + + // --- call Continuation.enter(Continuation c, boolean isContinue) + + // Emit compiled static call + // The call needs to be resolved. There's a special case for this in + // SharedRuntime::find_callee_info_helper() which calls + // LinkResolver::resolve_continuation_enter() which resolves the call to + // Continuation.enter(Continuation c, boolean isContinue). + address call_pc = __ pc(); + int start_offset = __ offset(); + // Put the entry point as a constant into the constant pool. + const address entry_point_toc_addr = __ address_constant(resolve_static_call, RelocationHolder::none); + const int entry_point_toc_offset = __ offset_to_method_toc(entry_point_toc_addr); + guarantee(entry_point_toc_addr != nullptr, "const section overflow"); + + // Emit the trampoline stub which will be related to the branch-and-link below. + address stub = __ emit_trampoline_stub(entry_point_toc_offset, start_offset); + guarantee(stub != nullptr, "no space for trampoline stub"); + + __ relocate(relocInfo::static_call_type); + // Note: At this point we do not have the address of the trampoline + // stub, and the entry point might be too far away for bl, so __ pc() + // serves as dummy and the bl will be patched later. + __ bl(__ pc()); + oop_maps->add_gc_map(__ pc() - start, map); + __ post_call_nop(); + + __ b(L_exit); + + // --- Thawing path + + __ bind(L_thaw); + __ add_const_optimized(R0, R29_TOC, MacroAssembler::offset_to_global_toc(StubRoutines::cont_thaw())); + __ mtctr(R0); + __ bctrl(); + oop_maps->add_gc_map(__ pc() - start, map->deep_copy()); + ContinuationEntry::_return_pc_offset = __ pc() - start; + __ post_call_nop(); + + // --- Normal exit (resolve/thawing) + + __ bind(L_exit); + continuation_enter_cleanup(masm); + + // Pop frame and return + DEBUG_ONLY(__ ld_ptr(R0, 0, R1_SP)); + __ addi(R1_SP, R1_SP, framesize_words*wordSize); + DEBUG_ONLY(__ cmpd(CCR0, R0, R1_SP)); + __ asm_assert_eq(FILE_AND_LINE ": inconsistent frame size"); + __ ld(R0, _abi0(lr), R1_SP); // Return pc + __ mtlr(R0); + __ blr(); + + // --- Exception handling path + + exception_offset = __ pc() - start; + + continuation_enter_cleanup(masm); + Register ex_pc = R17_tos; // nonvolatile register + Register ex_oop = R15_esp; // nonvolatile register + __ ld(ex_pc, _abi0(callers_sp), R1_SP); // Load caller's return pc + __ ld(ex_pc, _abi0(lr), ex_pc); + __ mr(ex_oop, R3_RET); // save return value containing the exception oop + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), R16_thread, ex_pc); + __ mtlr(R3_RET); // the exception handler + __ ld(R1_SP, _abi0(callers_sp), R1_SP); // remove enterSpecial frame + + // Continue at exception handler + // See OptoRuntime::generate_exception_blob for register arguments + __ mr(R3_ARG1, ex_oop); // pass exception oop + __ mr(R4_ARG2, ex_pc); // pass exception pc + __ blr(); + + // static stub for the call above + CodeBuffer* cbuf = masm->code_section()->outer(); + stub = CompiledStaticCall::emit_to_interp_stub(*cbuf, call_pc); + guarantee(stub != nullptr, "no space for static stub"); +} + +static void gen_continuation_yield(MacroAssembler* masm, + const VMRegPair* regs, + OopMapSet* oop_maps, + int& frame_complete, + int& framesize_words, + int& compiled_entry_offset) { + Register tmp = R10_ARG8; + + const int framesize_bytes = (int)align_up((int)frame::abi_reg_args_size, frame::alignment_in_bytes); + framesize_words = framesize_bytes / wordSize; + + address start = __ pc(); + compiled_entry_offset = __ pc() - start; + + // Save return pc and push entry frame + __ mflr(tmp); + __ std(tmp, _abi0(lr), R1_SP); // SP->lr = return_pc + __ push_frame(framesize_bytes , R0); // SP -= frame_size_in_bytes + + DEBUG_ONLY(__ block_comment("Frame Complete")); + frame_complete = __ pc() - start; + address last_java_pc = __ pc(); + + // This nop must be exactly at the PC we push into the frame info. + // We use this nop for fast CodeBlob lookup, associate the OopMap + // with it right away. + __ post_call_nop(); + OopMap* map = new OopMap(framesize_bytes / VMRegImpl::stack_slot_size, 1); + oop_maps->add_gc_map(last_java_pc - start, map); + + __ calculate_address_from_global_toc(tmp, last_java_pc); // will be relocated + __ set_last_Java_frame(R1_SP, tmp); + __ call_VM_leaf(Continuation::freeze_entry(), R16_thread, R1_SP); + __ reset_last_Java_frame(); + + Label L_pinned; + + __ cmpdi(CCR0, R3_RET, 0); + __ bne(CCR0, L_pinned); + + // Pop frames of continuation including this stub's frame + __ ld_ptr(R1_SP, JavaThread::cont_entry_offset(), R16_thread); + // The frame pushed by gen_continuation_enter is on top now again + continuation_enter_cleanup(masm); + + __ bind(L_pinned); // pinned -- return to caller + + // Pop frame and return + __ pop_frame(); + __ ld(R0, _abi0(lr), R1_SP); // Return pc + __ mtlr(R0); + __ blr(); +} + // --------------------------------------------------------------------------- // Generate a native wrapper for a given method. The method takes arguments // in the Java compiled code convention, marshals them to the native @@ -1640,6 +2020,65 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, BasicType *in_sig_bt, VMRegPair *in_regs, BasicType ret_type) { + if (method->is_continuation_native_intrinsic()) { + int exception_offset = -1; + OopMapSet* oop_maps = new OopMapSet(); + int frame_complete = -1; + int stack_slots = -1; + int interpreted_entry_offset = -1; + int vep_offset = -1; + if (method->is_continuation_enter_intrinsic()) { + gen_continuation_enter(masm, + in_regs, + exception_offset, + oop_maps, + frame_complete, + stack_slots, + interpreted_entry_offset, + vep_offset); + } else if (method->is_continuation_yield_intrinsic()) { + gen_continuation_yield(masm, + in_regs, + oop_maps, + frame_complete, + stack_slots, + vep_offset); + } else { + guarantee(false, "Unknown Continuation native intrinsic"); + } + +#ifdef ASSERT + if (method->is_continuation_enter_intrinsic()) { + assert(interpreted_entry_offset != -1, "Must be set"); + assert(exception_offset != -1, "Must be set"); + } else { + assert(interpreted_entry_offset == -1, "Must be unset"); + assert(exception_offset == -1, "Must be unset"); + } + assert(frame_complete != -1, "Must be set"); + assert(stack_slots != -1, "Must be set"); + assert(vep_offset != -1, "Must be set"); +#endif + + __ flush(); + nmethod* nm = nmethod::new_native_nmethod(method, + compile_id, + masm->code(), + vep_offset, + frame_complete, + stack_slots, + in_ByteSize(-1), + in_ByteSize(-1), + oop_maps, + exception_offset); + if (method->is_continuation_enter_intrinsic()) { + ContinuationEntry::set_enter_code(nm, interpreted_entry_offset); + } else if (method->is_continuation_yield_intrinsic()) { + _cont_doYield_stub = nm; + } + return nm; + } + if (method->is_method_handle_intrinsic()) { vmIntrinsics::ID iid = method->intrinsic_id(); intptr_t start = (intptr_t)__ pc(); @@ -2351,7 +2790,7 @@ nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, // This function returns the adjust size (in number of words) to a c2i adapter // activation for use during deoptimization. int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) { - return align_up((callee_locals - callee_parameters) * Interpreter::stackElementWords, frame::alignment_in_bytes); + return align_up((callee_locals - callee_parameters) * Interpreter::stackElementWords, frame::frame_alignment_in_words); } uint SharedRuntime::in_preserve_stack_slots() { diff --git a/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp b/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp index 4a6205832822a..8e78e12454bd9 100644 --- a/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp @@ -28,13 +28,10 @@ #include "runtime/frame.inline.hpp" #include "runtime/registerMap.hpp" -// Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap +// Java frames don't have callee saved registers, so we can use a smaller RegisterMap class SmallRegisterMap { public: static constexpr SmallRegisterMap* instance = nullptr; -private: - static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN - DEBUG_ONLY({ Unimplemented(); }) public: // as_RegisterMap is used when we didn't want to templatize and abstract over RegisterMap type to support SmallRegisterMap // Consider enhancing SmallRegisterMap to support those cases @@ -42,22 +39,31 @@ class SmallRegisterMap { RegisterMap* as_RegisterMap() { return nullptr; } RegisterMap* copy_to_RegisterMap(RegisterMap* map, intptr_t* sp) const { - Unimplemented(); + map->clear(); + map->set_include_argument_oops(this->include_argument_oops()); return map; } SmallRegisterMap() {} SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); +#ifdef ASSERT + for(int i = 0; i < RegisterMap::reg_count; i++) { + VMReg r = VMRegImpl::as_VMReg(i); + if (map->location(r, (intptr_t*)nullptr) != nullptr) { + assert(false, "Reg: %s", r->name()); // Should not reach here + } + } +#endif } inline address location(VMReg reg, intptr_t* sp) const { - Unimplemented(); - return NULL; + assert(false, "Reg: %s", reg->name()); + return nullptr; } - inline void set_location(VMReg reg, address loc) { assert_is_rfp(reg); } + // Should not reach here + inline void set_location(VMReg reg, address loc) { assert(false, "Reg: %s", reg->name()); } JavaThread* thread() const { #ifndef ASSERT diff --git a/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp b/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp index f3b28dac0dad7..ffccb00917538 100644 --- a/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp @@ -32,39 +32,52 @@ #ifdef ASSERT template inline bool StackChunkFrameStream::is_in_frame(void* p0) const { - Unimplemented(); - return true; + assert(!is_done(), ""); + assert(is_compiled(), ""); + intptr_t* p = (intptr_t*)p0; + int argsize = (_cb->as_compiled_method()->method()->num_stack_arg_slots() * VMRegImpl::stack_slot_size) >> LogBytesPerWord; + int frame_size = _cb->frame_size() + (argsize > 0 ? argsize + frame::metadata_words_at_top : 0); + return (p - unextended_sp()) >= 0 && (p - unextended_sp()) < frame_size; } #endif template inline frame StackChunkFrameStream::to_frame() const { - Unimplemented(); - return frame(); + if (is_done()) { + return frame(_sp, _sp, nullptr, nullptr, nullptr, nullptr, true); + } else { + // Compiled frames on heap don't have back links. See FreezeBase::patch_pd() and frame::setup(). + return frame(sp(), unextended_sp(), Interpreter::contains(pc()) ? fp() : nullptr, pc(), cb(), _oopmap, true); + } } template inline address StackChunkFrameStream::get_pc() const { - Unimplemented(); - return NULL; + assert(!is_done(), ""); + return (address)((frame::abi_minframe*) _sp)->lr; } template inline intptr_t* StackChunkFrameStream::fp() const { - Unimplemented(); - return NULL; + // See FreezeBase::patch_pd() and frame::setup() + assert((frame_kind == ChunkFrames::Mixed && is_interpreted()), ""); + intptr_t* fp_addr = (intptr_t*)&((frame::abi_minframe*)_sp)->callers_sp; + assert(*(intptr_t**)fp_addr != nullptr, ""); + // derelativize + return fp_addr + *fp_addr; } template inline intptr_t* StackChunkFrameStream::derelativize(int offset) const { - Unimplemented(); - return NULL; + intptr_t* fp = this->fp(); + assert(fp != nullptr, ""); + return fp + fp[offset]; } template inline intptr_t* StackChunkFrameStream::unextended_sp_for_interpreter_frame() const { - Unimplemented(); - return NULL; + assert_is_interpreted_and_frame_type_mixed(); + return derelativize(ijava_idx(esp)) + 1 - frame::metadata_words; // On PPC esp points to the next free slot } template @@ -75,37 +88,122 @@ intptr_t* StackChunkFrameStream::next_sp_for_interpreter_frame() con template inline void StackChunkFrameStream::next_for_interpreter_frame() { - Unimplemented(); + assert_is_interpreted_and_frame_type_mixed(); + if (derelativize(ijava_idx(locals)) + 1 >= _end) { + _unextended_sp = _end; + _sp = _end; + } else { + _unextended_sp = derelativize(ijava_idx(sender_sp)); + _sp = this->fp(); + } } +// Details for the comment on StackChunkFrameStream::frame_size() +// +// Interpreted caller frames get extended even if the callee is also +// interpreted. This is done to accomodate non-parameter locals. +// +// The size of a single frame is from the unextended sp to the bottom of the +// locals array. The combined size of caller/callee is the single size with the +// overlap deducted. The overlap is the size of the call parameters plus the +// size of the metadata at the sp (frame::metadata_words_at_top). +// +// +// Case 1: no metadata between a frame Case 2: metadata is located between +// and its locals a frame and its locals as on ppc64 +// +// | | L0 aka P0 | | | L0 aka P0 | +// | | : : | | | : : | +// | | : Pn | | | : Pn | +// | | : | | | : | +// | | Lm | | | Lm | +// | ======================== | |----------------------| +// S0 | | Frame F0 | | | Metadata@top | +// | | | S0 | | | +// | | | | | | +// | |----------------------| | | | +// || | L0 aka P0 | | ======================== +// over- || | : : | | | Frame F0 | +// lap || | : Pn |<- unext. SP | | | +// | | : | | | |<- bottom_of_locals +// | | Lm |<- SP | |----------------------| +// | ======================== || | L0 aka P0 | +// | | Frame F1 | || | : : | +// S1 | | | over- || | : Pn |<- unext. SP +// | | | lap || | : | + metadata_words_at_top +// | |----------------------| || | Lm | +// | | L0 aka P0 | || |----------------------| +// | | : : | || | Metadata@top | +// | | : Pn |<- unext. SP || | |<- unextended SP +// | : | | | | +// | Lm |<- SP | | |<- SP +// ======================== | ======================== +// | | Frame F1 | +// | | | +// | | | +// | |----------------------| +// overlap = size of stackargs S1 | | L0 aka P0 | +// | | : : | +// | | : Pn |<- unext. SP +// | | : | + metadata_words_at_top +// | | Lm | +// | |----------------------| +// | | Metadata@top | +// | | |<- unextended SP +// | | +// | |<- SP +// ======================== +// +// sizeof(Metadata@top) = frame::metadata_words_at_top +// bottom_of_locals = unext. sp + sizeof(Metadata@top) + stackargs +// overlap = bottom_of_locals - unext. sp +// = stackargs + sizeof(Metadata@top) template inline int StackChunkFrameStream::interpreter_frame_size() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + intptr_t* top = unextended_sp(); // later subtract argsize if callee is interpreted + intptr_t* bottom = derelativize(ijava_idx(locals)) + 1; + return (int)(bottom - top); } +// Size of stack args in words (P0..Pn above). Only valid if the caller is also +// interpreted. The function is also called if the caller is compiled but the +// result is not used in that case (same on x86). +// See also setting of sender_sp in ContinuationHelper::InterpretedFrame::patch_sender_sp() template inline int StackChunkFrameStream::interpreter_frame_stack_argsize() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + frame::ijava_state* state = (frame::ijava_state*)((uintptr_t)fp() - frame::ijava_state_size); + int diff = (int)(state->locals - (state->sender_sp + frame::metadata_words_at_top) + 1); + assert(diff == -frame::metadata_words_at_top || ((Method*)state->method)->size_of_parameters() == diff, + "size_of_parameters(): %d diff: %d sp: " PTR_FORMAT " fp:" PTR_FORMAT, + ((Method*)state->method)->size_of_parameters(), diff, p2i(sp()), p2i(fp())); + return diff; } template inline int StackChunkFrameStream::interpreter_frame_num_oops() const { - Unimplemented(); - return 0; + assert_is_interpreted_and_frame_type_mixed(); + ResourceMark rm; + InterpreterOopMap mask; + frame f = to_frame(); + f.interpreted_frame_oop_map(&mask); + return mask.num_oops() + + 1 // for the mirror oop + + ((intptr_t*)f.interpreter_frame_monitor_begin() + - (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); } template<> template<> inline void StackChunkFrameStream::update_reg_map_pd(RegisterMap* map) { - Unimplemented(); + // Nothing to do (no non-volatile registers in java calling convention) } template<> template<> inline void StackChunkFrameStream::update_reg_map_pd(RegisterMap* map) { - Unimplemented(); + // Nothing to do (no non-volatile registers in java calling convention) } template diff --git a/src/hotspot/cpu/ppc/stackChunkOop_ppc.inline.hpp b/src/hotspot/cpu/ppc/stackChunkOop_ppc.inline.hpp index ad8af0475185f..9afd5acb47c9f 100644 --- a/src/hotspot/cpu/ppc/stackChunkOop_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/stackChunkOop_ppc.inline.hpp @@ -25,12 +25,18 @@ #ifndef CPU_PPC_STACKCHUNKOOP_PPC_INLINE_HPP #define CPU_PPC_STACKCHUNKOOP_PPC_INLINE_HPP +#include "runtime/frame.inline.hpp" + inline void stackChunkOopDesc::relativize_frame_pd(frame& fr) const { - Unimplemented(); + if (fr.is_interpreted_frame()) { + fr.set_offset_fp(relativize_address(fr.fp())); + } } inline void stackChunkOopDesc::derelativize_frame_pd(frame& fr) const { - Unimplemented(); + if (fr.is_interpreted_frame()) { + fr.set_fp(derelativize_address(fr.offset_fp())); + } } #endif // CPU_PPC_STACKCHUNKOOP_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index cd90de8379126..eb8778f2900a7 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -36,6 +36,8 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/methodHandles.hpp" +#include "runtime/continuation.hpp" +#include "runtime/continuationEntry.inline.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/javaThread.hpp" @@ -323,6 +325,7 @@ class StubGenerator: public StubCodeGenerator { // pop frame and restore non-volatiles, LR and CR __ mr(R1_SP, r_entryframe_fp); + __ pop_cont_fastpath(); __ mtcr(r_cr); __ mtlr(r_lr); @@ -4501,22 +4504,105 @@ class StubGenerator: public StubCodeGenerator { #endif // VM_LITTLE_ENDIAN - address generate_cont_thaw() { + address generate_cont_thaw(const char* label, Continuation::thaw_kind kind) { if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + + bool return_barrier = Continuation::is_thaw_return_barrier(kind); + bool return_barrier_exception = Continuation::is_thaw_return_barrier_exception(kind); + + StubCodeMark mark(this, "StubRoutines", label); + + Register tmp1 = R10_ARG8; + Register tmp2 = R9_ARG7; + Register tmp3 = R8_ARG6; + Register nvtmp = R15_esp; // nonvolatile tmp register + FloatRegister nvftmp = F20; // nonvolatile fp tmp register + + address start = __ pc(); + + if (return_barrier) { + __ mr(nvtmp, R3_RET); __ fmr(nvftmp, F1_RET); // preserve possible return value from a method returning to the return barrier + DEBUG_ONLY(__ ld_ptr(tmp1, _abi0(callers_sp), R1_SP);) + __ ld_ptr(R1_SP, JavaThread::cont_entry_offset(), R16_thread); +#ifdef ASSERT + __ ld_ptr(tmp2, _abi0(callers_sp), R1_SP); + __ cmpd(CCR0, tmp1, tmp2); + __ asm_assert_eq(FILE_AND_LINE ": callers sp is corrupt"); +#endif + } +#ifdef ASSERT + __ ld_ptr(tmp1, JavaThread::cont_entry_offset(), R16_thread); + __ cmpd(CCR0, R1_SP, tmp1); + __ asm_assert_eq(FILE_AND_LINE ": incorrect R1_SP"); +#endif + + __ li(R4_ARG2, return_barrier ? 1 : 0); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, Continuation::prepare_thaw), R16_thread, R4_ARG2); + +#ifdef ASSERT + DEBUG_ONLY(__ ld_ptr(tmp1, JavaThread::cont_entry_offset(), R16_thread)); + DEBUG_ONLY(__ cmpd(CCR0, R1_SP, tmp1)); + __ asm_assert_eq(FILE_AND_LINE ": incorrect R1_SP"); +#endif + + // R3_RET contains the size of the frames to thaw, 0 if overflow or no more frames + Label thaw_success; + __ cmpdi(CCR0, R3_RET, 0); + __ bne(CCR0, thaw_success); + __ load_const_optimized(tmp1, (StubRoutines::throw_StackOverflowError_entry()), R0); + __ mtctr(tmp1); __ bctr(); + __ bind(thaw_success); + + __ addi(R3_RET, R3_RET, frame::abi_reg_args_size); // Large abi required for C++ calls. + __ neg(R3_RET, R3_RET); + // align down resulting in a smaller negative offset + __ clrrdi(R3_RET, R3_RET, exact_log2(frame::alignment_in_bytes)); + DEBUG_ONLY(__ mr(tmp1, R1_SP);) + __ resize_frame(R3_RET, tmp2); // make room for the thawed frames + + __ li(R4_ARG2, kind); + __ call_VM_leaf(Continuation::thaw_entry(), R16_thread, R4_ARG2); + __ mr(R1_SP, R3_RET); // R3_RET contains the SP of the thawed top frame + + if (return_barrier) { + // we're now in the caller of the frame that returned to the barrier + __ mr(R3_RET, nvtmp); __ fmr(F1_RET, nvftmp); // restore return value (no safepoint in the call to thaw, so even an oop return value should be OK) + } else { + // we're now on the yield frame (which is in an address above us b/c rsp has been pushed down) + __ li(R3_RET, 0); // return 0 (success) from doYield + } + + if (return_barrier_exception) { + Register ex_pc = R17_tos; // nonvolatile register + __ ld(ex_pc, _abi0(lr), R1_SP); // LR + __ mr(nvtmp, R3_RET); // save return value containing the exception oop + __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), R16_thread, ex_pc); + __ mtlr(R3_RET); // the exception handler + // See OptoRuntime::generate_exception_blob for register arguments + __ mr(R3_ARG1, nvtmp); // exception oop + __ mr(R4_ARG2, ex_pc); // exception pc + } else { + // We're "returning" into the topmost thawed frame; see Thaw::push_return_frame + __ ld(R0, _abi0(lr), R1_SP); // LR + __ mtlr(R0); + } + __ blr(); + + return start; } + address generate_cont_thaw() { + return generate_cont_thaw("Cont thaw", Continuation::thaw_top); + } + + // TODO: will probably need multiple return barriers depending on return type + address generate_cont_returnBarrier() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + return generate_cont_thaw("Cont thaw return barrier", Continuation::thaw_return_barrier); } address generate_cont_returnBarrier_exception() { - if (!Continuations::enabled()) return nullptr; - Unimplemented(); - return nullptr; + return generate_cont_thaw("Cont thaw return barrier exception", Continuation::thaw_return_barrier_exception); } #if INCLUDE_JFR @@ -4525,14 +4611,11 @@ class StubGenerator: public StubCodeGenerator { // It returns a jobject handle to the event writer. // The handle is dereferenced and the return value is the event writer oop. RuntimeStub* generate_jfr_write_checkpoint() { + CodeBuffer code("jfr_write_checkpoint", 512, 64); + MacroAssembler* _masm = new MacroAssembler(&code); + Register tmp1 = R10_ARG8; Register tmp2 = R9_ARG7; - int insts_size = 512; - int locs_size = 64; - CodeBuffer code("jfr_write_checkpoint", insts_size, locs_size); - OopMapSet* oop_maps = new OopMapSet(); - MacroAssembler* masm = new MacroAssembler(&code); - MacroAssembler* _masm = masm; int framesize = frame::abi_reg_args_size / VMRegImpl::stack_slot_size; address start = __ pc(); @@ -4557,11 +4640,13 @@ class StubGenerator: public StubCodeGenerator { __ mtlr(tmp1); __ blr(); + OopMapSet* oop_maps = new OopMapSet(); OopMap* map = new OopMap(framesize, 0); oop_maps->add_gc_map(calls_return_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_write_checkpoint", &code, frame_complete, + RuntimeStub::new_runtime_stub(code.name(), + &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; diff --git a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp index 260d99bef81c5..2c89caba2e10d 100644 --- a/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/templateInterpreterGenerator_ppc.cpp @@ -631,9 +631,7 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, default : ShouldNotReachHere(); } - __ restore_interpreter_state(R11_scratch1); // Sets R11_scratch1 = fp. - __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1); - __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); + __ restore_interpreter_state(R11_scratch1, false /*bcp_and_mdx_only*/, true /*restore_top_frame_sp*/); // Compiled code destroys templateTableBase, reload. __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R12_scratch2); @@ -702,7 +700,9 @@ address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, address entry = __ pc(); __ push(state); + __ push_cont_fastpath(); __ call_VM(noreg, runtime_entry); + __ pop_cont_fastpath(); __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos)); return entry; @@ -1943,9 +1943,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // Entry point if an method returns with a pending exception (rethrow). Interpreter::_rethrow_exception_entry = __ pc(); { - __ restore_interpreter_state(R11_scratch1); // Sets R11_scratch1 = fp. - __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1); - __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); + __ restore_interpreter_state(R11_scratch1, false /*bcp_and_mdx_only*/, true /*restore_top_frame_sp*/); // Compiled code destroys templateTableBase, reload. __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1); @@ -2036,6 +2034,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // we will reexecute the call that called us. __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*reload return_pc*/ return_pc, R11_scratch1, R12_scratch2); __ mtlr(return_pc); + __ pop_cont_fastpath(); __ blr(); // The non-deoptimized case. @@ -2047,9 +2046,8 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // Get out of the current method and re-execute the call that called us. __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ noreg, R11_scratch1, R12_scratch2); - __ restore_interpreter_state(R11_scratch1); - __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1); - __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); + __ pop_cont_fastpath(); + __ restore_interpreter_state(R11_scratch1, false /*bcp_and_mdx_only*/, true /*restore_top_frame_sp*/); if (ProfileInterpreter) { __ set_method_data_pointer_for_bcp(); __ ld(R11_scratch1, 0, R1_SP); @@ -2108,6 +2106,7 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // Remove the current activation. __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ noreg, R11_scratch1, R12_scratch2); + __ pop_cont_fastpath(); __ mr(R4_ARG2, return_pc); __ mtlr(R3_RET); diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index 1ea0114d5ea1f..2da3b2bf419b3 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -2146,7 +2146,9 @@ void TemplateTable::_return(TosState state) { __ andi_(R11_scratch1, R11_scratch1, SafepointMechanism::poll_bit()); __ beq(CCR0, no_safepoint); __ push(state); + __ push_cont_fastpath(); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)); + __ pop_cont_fastpath(); __ pop(state); __ bind(no_safepoint); } diff --git a/src/hotspot/cpu/riscv/continuationEntry_riscv.hpp b/src/hotspot/cpu/riscv/continuationEntry_riscv.hpp new file mode 100644 index 0000000000000..e6af5108218cf --- /dev/null +++ b/src/hotspot/cpu/riscv/continuationEntry_riscv.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_RISCV_CONTINUATIONENTRY_RISCV_HPP +#define CPU_RISCV_CONTINUATIONENTRY_RISCV_HPP + +class ContinuationEntryPD { + // empty +}; + +#endif // CPU_RISCV_CONTINUATIONENTRY_RISCV_HPP diff --git a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp index 74d94687df926..4b37b4c716cb9 100644 --- a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp @@ -92,6 +92,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) { Unimplemented(); } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + Unimplemented(); +} + inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Unimplemented(); } diff --git a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp index 1a35dfd902877..28ff3d40375f6 100644 --- a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp @@ -88,7 +88,7 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return NULL; } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { Unimplemented(); } diff --git a/src/hotspot/cpu/riscv/frame_riscv.hpp b/src/hotspot/cpu/riscv/frame_riscv.hpp index 2939c7211ad47..9560f9468e373 100644 --- a/src/hotspot/cpu/riscv/frame_riscv.hpp +++ b/src/hotspot/cpu/riscv/frame_riscv.hpp @@ -139,6 +139,13 @@ // size, in words, of frame metadata (e.g. pc and link) metadata_words = sender_sp_offset, + // size, in words, of metadata at frame bottom, i.e. it is not part of the + // caller/callee overlap + metadata_words_at_bottom = metadata_words, + // size, in words, of frame metadata at the frame top, i.e. it is located + // between a callee frame and its stack arguments, where it is part + // of the caller/callee overlap + metadata_words_at_top = 0, // in bytes frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment diff --git a/src/hotspot/cpu/s390/continuationEntry_s390.hpp b/src/hotspot/cpu/s390/continuationEntry_s390.hpp new file mode 100644 index 0000000000000..e4e611d2b1524 --- /dev/null +++ b/src/hotspot/cpu/s390/continuationEntry_s390.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_S390_CONTINUATIONENTRY_S390_HPP +#define CPU_S390_CONTINUATIONENTRY_S390_HPP + +class ContinuationEntryPD { + // empty +}; + +#endif // CPU_S390_CONTINUATIONENTRY_S390_HPP diff --git a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp index de6ace6823cce..a97e700ee3aa5 100644 --- a/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationFreezeThaw_s390.inline.hpp @@ -91,6 +91,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) { Unimplemented(); } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + Unimplemented(); +} + inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Unimplemented(); } diff --git a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp index 935689165204f..0a00cb69ad234 100644 --- a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp @@ -89,7 +89,7 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return NULL; } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { Unimplemented(); } diff --git a/src/hotspot/cpu/s390/frame_s390.hpp b/src/hotspot/cpu/s390/frame_s390.hpp index fffed836a7f7d..a7c53f23c7078 100644 --- a/src/hotspot/cpu/s390/frame_s390.hpp +++ b/src/hotspot/cpu/s390/frame_s390.hpp @@ -545,11 +545,13 @@ // for z/Architecture, too. // // Normal return address is the instruction following the branch. - pc_return_offset = 0, - metadata_words = 0, - frame_alignment = 16, + pc_return_offset = 0, + metadata_words = 0, + metadata_words_at_bottom = 0, + metadata_words_at_top = 0, + frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment - align_wiggle = 1 + align_wiggle = 1 }; static jint interpreter_frame_expression_stack_direction() { return -1; } diff --git a/src/hotspot/cpu/x86/continuationEntry_x86.hpp b/src/hotspot/cpu/x86/continuationEntry_x86.hpp new file mode 100644 index 0000000000000..87661531932f5 --- /dev/null +++ b/src/hotspot/cpu/x86/continuationEntry_x86.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_X86_CONTINUATIONENTRY_X86_HPP +#define CPU_X86_CONTINUATIONENTRY_X86_HPP + +class ContinuationEntryPD { + // empty +}; + +#endif // CPU_X86_CONTINUATIONENTRY_X86_HPP diff --git a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp index b95bd25696adc..0b2b51f6e45cf 100644 --- a/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationFreezeThaw_x86.inline.hpp @@ -193,6 +193,12 @@ inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Prefetch::read(start, size - 64); } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + // Fast path depends on !PreserveFramePointer. See can_thaw_fast(). + assert(!PreserveFramePointer, "Frame pointers need to be fixed"); +} + void ThawBase::patch_chunk_pd(intptr_t* sp) { intptr_t* fp = _cont.entryFP(); *(intptr_t**)(sp - frame::sender_sp_offset) = fp; diff --git a/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp b/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp index 15c031b95e281..39184d2e3c703 100644 --- a/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp @@ -100,7 +100,8 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return (address*)(f.fp() + frame::return_addr_offset); } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { + intptr_t* sp = caller.unextended_sp(); assert(f.is_interpreted_frame(), ""); intptr_t* la = f.addr_at(frame::interpreter_frame_sender_sp_offset); *la = f.is_heap_frame() ? (intptr_t)(sp - f.fp()) : (intptr_t)sp; diff --git a/src/hotspot/cpu/x86/frame_x86.hpp b/src/hotspot/cpu/x86/frame_x86.hpp index 8d11d7a4acf45..e14b3323b71c3 100644 --- a/src/hotspot/cpu/x86/frame_x86.hpp +++ b/src/hotspot/cpu/x86/frame_x86.hpp @@ -97,7 +97,15 @@ // size, in words, of frame metadata (e.g. pc and link) metadata_words = sender_sp_offset, - // compiled frame alignment, in bytes + // size, in words, of metadata at frame bottom, i.e. it is not part of the + // caller/callee overlap + metadata_words_at_bottom = metadata_words, + // size, in words, of frame metadata at the frame top, i.e. it is located + // between a callee frame and its stack arguments, where it is part + // of the caller/callee overlap + metadata_words_at_top = 0, + // size, in words, of frame metadata at the frame top that needs + // to be reserved for callee functions in the runtime frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 diff --git a/src/hotspot/cpu/zero/continuationEntry_zero.hpp b/src/hotspot/cpu/zero/continuationEntry_zero.hpp new file mode 100644 index 0000000000000..fa1a1d0ceaed3 --- /dev/null +++ b/src/hotspot/cpu/zero/continuationEntry_zero.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef CPU_ZERO_CONTINUATIONENTRY_ZERO_HPP +#define CPU_ZERO_CONTINUATIONENTRY_ZERO_HPP + +class ContinuationEntryPD { + // empty +}; + +#endif // CPU_ZERO_CONTINUATIONENTRY_ZERO_HPP diff --git a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp index 76a7d4454ba78..69d77500fa77c 100644 --- a/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationFreezeThaw_zero.inline.hpp @@ -91,6 +91,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) { Unimplemented(); } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + Unimplemented(); +} + inline void ThawBase::prefetch_chunk_pd(void* start, int size) { Unimplemented(); } diff --git a/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp b/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp index c04e128f04e54..a7e3119ca2da1 100644 --- a/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp @@ -87,7 +87,7 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return NULL; } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { Unimplemented(); } diff --git a/src/hotspot/cpu/zero/frame_zero.hpp b/src/hotspot/cpu/zero/frame_zero.hpp index e5cce2dc4c845..87511ab212e1f 100644 --- a/src/hotspot/cpu/zero/frame_zero.hpp +++ b/src/hotspot/cpu/zero/frame_zero.hpp @@ -32,6 +32,13 @@ enum { pc_return_offset = 0, metadata_words = 0, + // size, in words, of metadata at frame bottom, i.e. it is not part of the + // caller/callee overlap + metadata_words_at_bottom = metadata_words, + // size, in words, of frame metadata at the frame top, i.e. it is located + // between a callee frame and its stack arguments, where it is part + // of the caller/callee overlap + metadata_words_at_top = 0, frame_alignment = 16, // size, in words, of maximum shift in frame position due to alignment align_wiggle = 1 diff --git a/src/hotspot/share/oops/instanceStackChunkKlass.hpp b/src/hotspot/share/oops/instanceStackChunkKlass.hpp index a7c819b35c951..d16c0f00c480f 100644 --- a/src/hotspot/share/oops/instanceStackChunkKlass.hpp +++ b/src/hotspot/share/oops/instanceStackChunkKlass.hpp @@ -50,45 +50,51 @@ class MemRegion; Chunk layout: - +-------------------+ - | | - | oop bitmap | - | | - | ----------------- | - | | - | [empty] | - | | - -|===================| - / | | - | | caller stack args | argsize - | | | words - | | ----------------- | - | | | - ^ | | frame | - | | | | - | size | ----------------- | - | words | | - | | | frame | - | | | | - Address | | | ----------------- | - | | | | - | | | frame | - | | | | - | | | callee stack args | - | | | ----------------- |<--\ - | | | pc | | - | | | rbp | | - | | | | | - | | | [empty] | | - | \ | | | - - |===================| | - | int maxSize | | - | long pc | | - header | byte flags | | - | int argsize | | - | int sp +---/ - | int size | - +-------------------+ + +--------------------------------+ + | | + | oop bitmap | + | | + | ------------------------------ | + | | + | [empty] | + | | + -|================================| + / | | + | | caller stack args | argsize + | | [metadata at frame top (1)] | + frame::metadata_words_at_top + | | ------------------------------ | words + | | [metadata at frame bottom (2)] | + ^ | | frame | + | | | | + | size | ------------------------------ | + | words | | + | | | frame | + | | | | + Address | | | ------------------------------ | + | | | | + | | | frame | + | | | | + | | | callee stack args | + | | | [metadata at frame top (1)] |<--\ + | | | ------------------------------ | | + | | | [metadata at frame bottom (2) | | + | | | i.e. rbp, pc] | | + | | | | | + | | | [empty] | | + | \ | | | + - |================================| | + | int maxSize | | + | long pc | | + header | byte flags | | + | int argsize | | + | int sp +---/ + | int size | + +--------------------------------+ + + (1) Metadata at frame top (see frame::metadata_words_at_top) + Used on ppc64, empty on x86_64, aarch64 + (2) Metadata at the frame bottom (see frame::metadata_words_at_bottom) + Used on x86_64 (saved rbp, ret.addr.), aarch64, empty on ppc64 ************************************************/ diff --git a/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp b/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp index 59150ca92cc86..4fdc6141e07fc 100644 --- a/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp +++ b/src/hotspot/share/oops/instanceStackChunkKlass.inline.hpp @@ -105,7 +105,7 @@ void InstanceStackChunkKlass::oop_oop_iterate_header_bounded(stackChunkOop chunk template void InstanceStackChunkKlass::oop_oop_iterate_stack_bounded(stackChunkOop chunk, OopClosureType* closure, MemRegion mr) { if (chunk->has_bitmap()) { - intptr_t* start = chunk->sp_address() - frame::metadata_words; + intptr_t* start = chunk->sp_address() - frame::metadata_words_at_bottom; intptr_t* end = chunk->end_address(); // mr.end() can actually be less than start. In that case, we only walk the metadata if ((intptr_t*)mr.start() > start) { @@ -123,7 +123,7 @@ void InstanceStackChunkKlass::oop_oop_iterate_stack_bounded(stackChunkOop chunk, template void InstanceStackChunkKlass::oop_oop_iterate_stack(stackChunkOop chunk, OopClosureType* closure) { if (chunk->has_bitmap()) { - oop_oop_iterate_stack_with_bitmap(chunk, closure, chunk->sp_address() - frame::metadata_words, chunk->end_address()); + oop_oop_iterate_stack_with_bitmap(chunk, closure, chunk->sp_address() - frame::metadata_words_at_bottom, chunk->end_address()); } else { oop_oop_iterate_stack_slow(chunk, closure, chunk->range()); } diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp index 09b04a5787837..00eb45069b1b4 100644 --- a/src/hotspot/share/oops/stackChunkOop.cpp +++ b/src/hotspot/share/oops/stackChunkOop.cpp @@ -498,7 +498,7 @@ class VerifyStackChunkFrameClosure { int num_oops = f.num_oops(); assert(num_oops >= 0, ""); - _argsize = f.stack_argsize(); + _argsize = f.stack_argsize() + frame::metadata_words_at_top; _size += fsize; _num_oops += num_oops; if (f.is_interpreted()) { @@ -602,7 +602,7 @@ bool stackChunkOopDesc::verify(size_t* out_size, int* out_oops, int* out_frames, assert(closure._size <= size + argsize() + frame::metadata_words, "size: %d argsize: %d closure.size: %d end sp: " PTR_FORMAT " start sp: %d chunk size: %d", size, argsize(), closure._size, closure._sp - start_address(), sp(), stack_size()); - assert(argsize() == closure._argsize, + assert(argsize() == closure._argsize - (closure._num_frames > 0 ? frame::metadata_words_at_top : 0), "argsize(): %d closure.argsize: %d closure.callee_interpreted: %d", argsize(), closure._argsize, closure._callee_interpreted); @@ -633,13 +633,13 @@ bool stackChunkOopDesc::verify(size_t* out_size, int* out_oops, int* out_frames, if (UseCompressedOops) { StackChunkVerifyBitmapClosure bitmap_closure(this); bitmap().iterate(&bitmap_closure, - bit_index_for((narrowOop*)(sp_address() - frame::metadata_words)), + bit_index_for((narrowOop*)(sp_address() - frame::metadata_words_at_bottom)), bit_index_for((narrowOop*)end_address())); oop_count = bitmap_closure._count; } else { StackChunkVerifyBitmapClosure bitmap_closure(this); bitmap().iterate(&bitmap_closure, - bit_index_for((oop*)(sp_address() - frame::metadata_words)), + bit_index_for((oop*)(sp_address() - frame::metadata_words_at_bottom)), bit_index_for((oop*)end_address())); oop_count = bitmap_closure._count; } diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index 15d848bb82a3b..6db1a7b3b85a2 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -98,7 +98,7 @@ inline void stackChunkOopDesc::set_cont_raw(oop value) { jdk_internal_vm_Sta template inline void stackChunkOopDesc::set_cont_access(oop value) { jdk_internal_vm_StackChunk::set_cont_access(this, value); } -inline int stackChunkOopDesc::bottom() const { return stack_size() - argsize(); } +inline int stackChunkOopDesc::bottom() const { return stack_size() - argsize() - frame::metadata_words_at_top; } inline HeapWord* stackChunkOopDesc::start_of_stack() const { return (HeapWord*)(cast_from_oop(as_oop()) + InstanceStackChunkKlass::offset_of_stack()); @@ -123,7 +123,7 @@ inline intptr_t* stackChunkOopDesc::from_offset(int offset) const { inline bool stackChunkOopDesc::is_empty() const { assert(sp() <= stack_size(), ""); - assert((sp() == stack_size()) == (sp() >= stack_size() - argsize()), + assert((sp() == stack_size()) == (sp() >= stack_size() - argsize() - frame::metadata_words_at_top), "sp: %d size: %d argsize: %d", sp(), stack_size(), argsize()); return sp() == stack_size(); } @@ -135,8 +135,8 @@ inline bool stackChunkOopDesc::is_in_chunk(void* p) const { } bool stackChunkOopDesc::is_usable_in_chunk(void* p) const { -#if (defined(X86) || defined(AARCH64)) && !defined(ZERO) - HeapWord* start = (HeapWord*)start_address() + sp() - frame::sender_sp_offset; +#if (defined(X86) || defined(AARCH64) || defined(PPC64)) && !defined(ZERO) + HeapWord* start = (HeapWord*)start_address() + sp() - frame::metadata_words_at_bottom; #else Unimplemented(); HeapWord* start = NULL; diff --git a/src/hotspot/share/runtime/continuation.cpp b/src/hotspot/share/runtime/continuation.cpp index 3903521cc448c..59e9b5617dcfe 100644 --- a/src/hotspot/share/runtime/continuation.cpp +++ b/src/hotspot/share/runtime/continuation.cpp @@ -239,7 +239,7 @@ frame Continuation::continuation_parent_frame(RegisterMap* map) { map->set_stack_chunk(nullptr); -#if (defined(X86) || defined(AARCH64)) && !defined(ZERO) +#if (defined(X86) || defined(AARCH64) || defined(PPC64)) && !defined(ZERO) frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC()); #else frame sender = frame(); diff --git a/src/hotspot/share/runtime/continuationEntry.hpp b/src/hotspot/share/runtime/continuationEntry.hpp index 6914a6795fce3..6557a199b8668 100644 --- a/src/hotspot/share/runtime/continuationEntry.hpp +++ b/src/hotspot/share/runtime/continuationEntry.hpp @@ -30,6 +30,8 @@ #include "runtime/continuation.hpp" #include "utilities/sizes.hpp" +#include CPU_HEADER(continuationEntry) + class CompiledMethod; class JavaThread; class OopMap; @@ -37,6 +39,7 @@ class RegisterMap; // Metadata stored in the continuation entry frame class ContinuationEntry { + ContinuationEntryPD _pd; #ifdef ASSERT private: static const int COOKIE_VALUE = 0x1234; @@ -66,6 +69,8 @@ class ContinuationEntry { oopDesc* _cont; oopDesc* _chunk; int _flags; + // Size in words of the stack arguments of the bottom frame on stack if compiled 0 otherwise. + // The caller (if there is one) is the still frozen top frame in the StackChunk. int _argsize; intptr_t* _parent_cont_fastpath; #ifdef _LP64 diff --git a/src/hotspot/share/runtime/continuationEntry.inline.hpp b/src/hotspot/share/runtime/continuationEntry.inline.hpp index e8a0995741cef..ea61616e54df7 100644 --- a/src/hotspot/share/runtime/continuationEntry.inline.hpp +++ b/src/hotspot/share/runtime/continuationEntry.inline.hpp @@ -34,7 +34,9 @@ #include CPU_HEADER_INLINE(continuationEntry) inline intptr_t* ContinuationEntry::bottom_sender_sp() const { - intptr_t* sp = entry_sp() - argsize(); + // the entry frame is extended if the bottom frame has stack arguments + int entry_frame_extension = argsize() > 0 ? argsize() + frame::metadata_words_at_top : 0; + intptr_t* sp = entry_sp() - entry_frame_extension; #ifdef _LP64 sp = align_down(sp, frame::frame_alignment); #endif diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 2ef48618ccbe3..f1d82bee7cd82 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -437,7 +437,7 @@ class FreezeBase : public StackObj { bool is_empty(stackChunkOop chunk) { // during freeze, the chunk is in an intermediate state (after setting the chunk's argsize but before setting its // ultimate sp) so we use this instead of stackChunkOopDesc::is_empty - return chunk->sp() >= chunk->stack_size() - chunk->argsize(); + return chunk->sp() >= chunk->stack_size() - chunk->argsize() - frame::metadata_words_at_top; } #endif }; @@ -468,7 +468,7 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* assert(!Interpreter::contains(_cont.entryPC()), ""); - _bottom_address = _cont.entrySP() - _cont.argsize(); + _bottom_address = _cont.entrySP() - _cont.entry_frame_extension(); #ifdef _LP64 if (((intptr_t)_bottom_address & 0xf) != 0) { _bottom_address--; @@ -484,12 +484,14 @@ FreezeBase::FreezeBase(JavaThread* thread, ContinuationWrapper& cont, intptr_t* assert(_cont.chunk_invariant(), ""); assert(!Interpreter::contains(_cont.entryPC()), ""); - static const int doYield_stub_frame_size = frame::metadata_words; + static const int doYield_stub_frame_size = NOT_PPC64(frame::metadata_words) + PPC64_ONLY(frame::abi_reg_args_size >> LogBytesPerWord); assert(SharedRuntime::cont_doYield_stub()->frame_size() == doYield_stub_frame_size, ""); // properties of the continuation on the stack; all sizes are in words _cont_stack_top = frame_sp + doYield_stub_frame_size; // we don't freeze the doYield stub frame - _cont_stack_bottom = _cont.entrySP() - ContinuationHelper::frame_align_words(_cont.argsize()); // see alignment in thaw + _cont_stack_bottom = _cont.entrySP() + (_cont.argsize() == 0 ? frame::metadata_words_at_top : 0) + - ContinuationHelper::frame_align_words(_cont.argsize()); // see alignment in thaw log_develop_trace(continuations)("freeze size: %d argsize: %d top: " INTPTR_FORMAT " bottom: " INTPTR_FORMAT, cont_size(), _cont.argsize(), p2i(_cont_stack_top), p2i(_cont_stack_bottom)); @@ -554,10 +556,7 @@ int FreezeBase::size_if_fast_freeze_available() { return 0; } - assert(SharedRuntime::cont_doYield_stub()->frame_size() == frame::metadata_words, ""); - int total_size_needed = cont_size(); - const int chunk_sp = chunk->sp(); // argsize can be nonzero if we have a caller, but the caller could be in a non-empty parent chunk, @@ -565,10 +564,10 @@ int FreezeBase::size_if_fast_freeze_available() { // Consider leaving the chunk's argsize set when emptying it and removing the following branch, // although that would require changing stackChunkOopDesc::is_empty if (chunk_sp < chunk->stack_size()) { - total_size_needed -= _cont.argsize(); + total_size_needed -= _cont.argsize() + frame::metadata_words_at_top; } - int chunk_free_room = chunk_sp - frame::metadata_words; + int chunk_free_room = chunk_sp - frame::metadata_words_at_bottom; bool available = chunk_free_room >= total_size_needed; log_develop_trace(continuations)("chunk available: %s size: %d argsize: %d top: " INTPTR_FORMAT " bottom: " INTPTR_FORMAT, available ? "yes" : "no" , total_size_needed, _cont.argsize(), p2i(_cont_stack_top), p2i(_cont_stack_bottom)); @@ -588,13 +587,14 @@ void FreezeBase::freeze_fast_existing_chunk() { assert(*(address*)(chunk->sp_address() - frame::sender_sp_ret_address_offset()) == chunk->pc(), ""); // the chunk's sp before the freeze, adjusted to point beyond the stack-passed arguments in the topmost frame - const int chunk_start_sp = chunk->sp() + _cont.argsize(); // we overlap; we'll overwrite the chunk's top frame's callee arguments + // we overlap; we'll overwrite the chunk's top frame's callee arguments + const int chunk_start_sp = chunk->sp() + _cont.argsize() + frame::metadata_words_at_top; assert(chunk_start_sp <= chunk->stack_size(), "sp not pointing into stack"); // increase max_size by what we're freezing minus the overlap - chunk->set_max_thawing_size(chunk->max_thawing_size() + cont_size() - _cont.argsize()); + chunk->set_max_thawing_size(chunk->max_thawing_size() + cont_size() - _cont.argsize() - frame::metadata_words_at_top); - intptr_t* const bottom_sp = _cont_stack_bottom - _cont.argsize(); + intptr_t* const bottom_sp = _cont_stack_bottom - _cont.argsize() - frame::metadata_words_at_top; assert(bottom_sp == _bottom_address, ""); // Because the chunk isn't empty, we know there's a caller in the chunk, therefore the bottom-most frame // should have a return barrier (installed back when we thawed it). @@ -671,13 +671,13 @@ void FreezeBase::freeze_fast_copy(stackChunkOop chunk, int chunk_start_sp CONT_J log_develop_trace(continuations)("freeze_fast start: " INTPTR_FORMAT " sp: %d chunk_top: " INTPTR_FORMAT, p2i(chunk->start_address()), chunk_new_sp, p2i(chunk_top)); - intptr_t* from = _cont_stack_top - frame::metadata_words; - intptr_t* to = chunk_top - frame::metadata_words; - copy_to_chunk(from, to, cont_size() + frame::metadata_words); + intptr_t* from = _cont_stack_top - frame::metadata_words_at_bottom; + intptr_t* to = chunk_top - frame::metadata_words_at_bottom; + copy_to_chunk(from, to, cont_size() + frame::metadata_words_at_bottom); // Because we're not patched yet, the chunk is now in a bad state // patch return pc of the bottom-most frozen frame (now in the chunk) with the actual caller's return address - intptr_t* chunk_bottom_sp = chunk_top + cont_size() - _cont.argsize(); + intptr_t* chunk_bottom_sp = chunk_top + cont_size() - _cont.argsize() - frame::metadata_words_at_top; assert(_empty || *(address*)(chunk_bottom_sp-frame::sender_sp_ret_address_offset()) == StubRoutines::cont_returnBarrier(), ""); *(address*)(chunk_bottom_sp - frame::sender_sp_ret_address_offset()) = chunk->pc(); @@ -783,6 +783,7 @@ frame FreezeBase::freeze_start_frame_safepoint_stub(frame f) { return f; } +// The parameter callee_argsize includes metadata that has to be part of caller/callee overlap. NOINLINE freeze_result FreezeBase::recurse_freeze(frame& f, frame& caller, int callee_argsize, bool callee_interpreted, bool top) { assert(f.unextended_sp() < _bottom_address, ""); // see recurse_freeze_java_frame assert(f.is_interpreted_frame() || ((top && _preempt) == ContinuationHelper::Frame::is_stub(f.cb())), ""); @@ -812,6 +813,8 @@ NOINLINE freeze_result FreezeBase::recurse_freeze(frame& f, frame& caller, int c } } +// The parameter callee_argsize includes metadata that has to be part of caller/callee overlap. +// See also StackChunkFrameStream::frame_size() template inline freeze_result FreezeBase::recurse_freeze_java_frame(const frame& f, frame& caller, int fsize, int argsize) { assert(FKind::is_instance(f), ""); @@ -860,7 +863,10 @@ inline void FreezeBase::after_freeze_java_frame(const frame& hf, bool is_bottom_ } } -freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, int argsize) { +// The parameter argsize_md includes metadata that has to be part of caller/callee overlap. +// See also StackChunkFrameStream::frame_size() +freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, int argsize_md) { + int argsize = argsize_md - frame::metadata_words_at_top; assert(callee.is_interpreted_frame() || callee.cb()->as_nmethod()->is_osr_method() || argsize == _cont.argsize(), "argsize: %d cont.argsize: %d", argsize, _cont.argsize()); @@ -889,7 +895,7 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in unextended_sp = chunk->to_offset(StackChunkFrameStream(chunk).unextended_sp()); bool top_interpreted = Interpreter::contains(chunk->pc()); if (callee.is_interpreted_frame() == top_interpreted) { - overlap = argsize; + overlap = argsize_md; } } } @@ -936,7 +942,7 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in // Install new chunk _cont.set_tail(chunk); - int sp = chunk->stack_size() - argsize; + int sp = chunk->stack_size() - argsize_md; chunk->set_sp(sp); chunk->set_argsize(argsize); assert(is_empty(chunk), ""); @@ -944,7 +950,7 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in // REUSE EXISTING CHUNK log_develop_trace(continuations)("Reusing chunk mixed: %d empty: %d", chunk->has_mixed_frames(), chunk->is_empty()); if (chunk->is_empty()) { - int sp = chunk->stack_size() - argsize; + int sp = chunk->stack_size() - argsize_md; chunk->set_sp(sp); chunk->set_argsize(argsize); _freeze_size += overlap; @@ -977,7 +983,8 @@ freeze_result FreezeBase::finalize_freeze(const frame& callee, frame& caller, in // The topmost existing frame in the chunk; or an empty frame if the chunk is empty caller = StackChunkFrameStream(chunk).to_frame(); - DEBUG_ONLY(_last_write = caller.unextended_sp() + (empty_chunk ? argsize : overlap);) + DEBUG_ONLY(_last_write = caller.unextended_sp() + (empty_chunk ? argsize_md : overlap);) + assert(chunk->is_in_chunk(_last_write - _freeze_size), "last_write-size: " INTPTR_FORMAT " start: " INTPTR_FORMAT, p2i(_last_write-_freeze_size), p2i(chunk->start_address())); #ifdef ASSERT @@ -1014,7 +1021,7 @@ void FreezeBase::patch(const frame& f, frame& hf, const frame& caller, bool is_b if (f.is_interpreted_frame()) { assert(hf.is_heap_frame(), "should be"); - ContinuationHelper::InterpretedFrame::patch_sender_sp(hf, caller.unextended_sp()); + ContinuationHelper::InterpretedFrame::patch_sender_sp(hf, caller); } #ifdef ASSERT @@ -1039,8 +1046,10 @@ static void verify_frame_top(const frame& f, intptr_t* top) { } #endif // ASSERT +// The parameter callee_argsize includes metadata that has to be part of caller/callee overlap. +// See also StackChunkFrameStream::frame_size() NOINLINE freeze_result FreezeBase::recurse_freeze_interpreted_frame(frame& f, frame& caller, - int callee_argsize, + int callee_argsize /* incl. metadata */, bool callee_interpreted) { adjust_interpreted_frame_unextended_sp(f); @@ -1055,7 +1064,8 @@ NOINLINE freeze_result FreezeBase::recurse_freeze_interpreted_frame(frame& f, fr DEBUG_ONLY(verify_frame_top(f, stack_frame_top)); Method* frame_method = ContinuationHelper::Frame::frame_method(f); - const int argsize = ContinuationHelper::InterpretedFrame::stack_argsize(f); + // including metadata between f and its args + const int argsize = ContinuationHelper::InterpretedFrame::stack_argsize(f) + frame::metadata_words_at_top; log_develop_trace(continuations)("recurse_freeze_interpreted_frame %s _size: %d fsize: %d argsize: %d", frame_method->name_and_sig_as_C_string(), _freeze_size, fsize, argsize); @@ -1073,7 +1083,7 @@ NOINLINE freeze_result FreezeBase::recurse_freeze_interpreted_frame(frame& f, fr DEBUG_ONLY(before_freeze_java_frame(f, caller, fsize, 0, is_bottom_frame);) frame hf = new_heap_frame(f, caller); - _total_align_size += frame::align_wiggle; // add alignment room for internal interpreted frame alignment om AArch64 + _total_align_size += frame::align_wiggle; // add alignment room for internal interpreted frame alignment on AArch64/PPC64 intptr_t* heap_frame_top = ContinuationHelper::InterpretedFrame::frame_top(hf, callee_argsize, callee_interpreted); intptr_t* heap_frame_bottom = ContinuationHelper::InterpretedFrame::frame_bottom(hf); @@ -1098,11 +1108,16 @@ NOINLINE freeze_result FreezeBase::recurse_freeze_interpreted_frame(frame& f, fr return freeze_ok; } -freeze_result FreezeBase::recurse_freeze_compiled_frame(frame& f, frame& caller, int callee_argsize, bool callee_interpreted) { +// The parameter callee_argsize includes metadata that has to be part of caller/callee overlap. +// See also StackChunkFrameStream::frame_size() +freeze_result FreezeBase::recurse_freeze_compiled_frame(frame& f, frame& caller, + int callee_argsize /* incl. metadata */, + bool callee_interpreted) { // The frame's top never includes the stack arguments to the callee intptr_t* const stack_frame_top = ContinuationHelper::CompiledFrame::frame_top(f, callee_argsize, callee_interpreted); intptr_t* const stack_frame_bottom = ContinuationHelper::CompiledFrame::frame_bottom(f); - const int argsize = ContinuationHelper::CompiledFrame::stack_argsize(f); + // including metadata between f and its stackargs + const int argsize = ContinuationHelper::CompiledFrame::stack_argsize(f) + frame::metadata_words_at_top; const int fsize = stack_frame_bottom + argsize - stack_frame_top; log_develop_trace(continuations)("recurse_freeze_compiled_frame %s _size: %d fsize: %d argsize: %d", @@ -1662,6 +1677,7 @@ class Thaw : public ThawBase { inline intptr_t* thaw(Continuation::thaw_kind kind); NOINLINE intptr_t* thaw_fast(stackChunkOop chunk); + inline void patch_caller_links(intptr_t* sp, intptr_t* bottom); }; template @@ -1684,24 +1700,26 @@ class ReconstructedStack : public StackObj { int _thaw_size; int _argsize; public: - ReconstructedStack(intptr_t* base, int thaw_size, int argsize) : _base(base), _thaw_size(thaw_size), _argsize(argsize) { + ReconstructedStack(intptr_t* base, int thaw_size, int argsize) + : _base(base), _thaw_size(thaw_size - (argsize == 0 ? frame::metadata_words_at_top : 0)), _argsize(argsize) { // The only possible source of misalignment is stack-passed arguments b/c compiled frames are 16-byte aligned. assert(argsize != 0 || (_base - _thaw_size) == ContinuationHelper::frame_align_pointer(_base - _thaw_size), ""); // We're at most one alignment word away from entrySP - assert(_base - 1 <= top() + total_size() + frame::metadata_words, "missed entry frame"); + assert(_base - 1 <= top() + total_size() + frame::metadata_words_at_bottom, "missed entry frame"); } int thaw_size() const { return _thaw_size; } int argsize() const { return _argsize; } + int entry_frame_extension() const { return _argsize + (_argsize > 0 ? frame::metadata_words_at_top : 0); } // top and bottom stack pointers intptr_t* sp() const { return ContinuationHelper::frame_align_pointer(_base - _thaw_size); } - intptr_t* bottom_sp() const { return ContinuationHelper::frame_align_pointer(_base - _argsize); } + intptr_t* bottom_sp() const { return ContinuationHelper::frame_align_pointer(_base - entry_frame_extension()); } // several operations operate on the totality of the stack being reconstructed, // including the metadata words - intptr_t* top() const { return sp() - frame::metadata_words; } - int total_size() const { return _thaw_size + frame::metadata_words; } + intptr_t* top() const { return sp() - frame::metadata_words_at_bottom; } + int total_size() const { return _thaw_size + frame::metadata_words_at_bottom; } }; inline void ThawBase::clear_chunk(stackChunkOop chunk) { @@ -1736,7 +1754,7 @@ inline void ThawBase::clear_chunk(stackChunkOop chunk) { assert(empty == chunk->is_empty(), ""); // returns the size required to store the frame on stack, and because it is a // compiled frame, it must include a copy of the arguments passed by the caller - return frame_size + argsize; + return frame_size + argsize + frame::metadata_words_at_top; } void ThawBase::copy_from_chunk(intptr_t* from, intptr_t* to, int size) { @@ -1797,13 +1815,13 @@ NOINLINE intptr_t* Thaw::thaw_fast(stackChunkOop chunk) { const bool is_last = empty && chunk->is_parent_null(); assert(!is_last || argsize == 0, ""); - log_develop_trace(continuations)("thaw_fast partial: %d is_last: %d empty: %d size: %d argsize: %d", - partial, is_last, empty, thaw_size, argsize); + log_develop_trace(continuations)("thaw_fast partial: %d is_last: %d empty: %d size: %d argsize: %d entrySP: " PTR_FORMAT, + partial, is_last, empty, thaw_size, argsize, p2i(_cont.entrySP())); ReconstructedStack rs(_cont.entrySP(), thaw_size, argsize); - // also copy metadata words - copy_from_chunk(chunk_sp - frame::metadata_words, rs.top(), rs.total_size()); + // also copy metadata words at frame bottom + copy_from_chunk(chunk_sp - frame::metadata_words_at_bottom, rs.top(), rs.total_size()); // update the ContinuationEntry _cont.set_argsize(argsize); @@ -1813,6 +1831,9 @@ NOINLINE intptr_t* Thaw::thaw_fast(stackChunkOop chunk) { // install the return barrier if not last frame, or the entry's pc if last patch_return(rs.bottom_sp(), is_last); + // insert the back links from callee to caller frames + patch_caller_links(rs.top(), rs.top() + rs.total_size()); + assert(is_last == _cont.is_empty(), ""); assert(_cont.chunk_invariant(), ""); @@ -1999,7 +2020,7 @@ inline void ThawBase::patch(frame& f, const frame& caller, bool bottom) { patch_pd(f, caller); if (f.is_interpreted_frame()) { - ContinuationHelper::InterpretedFrame::patch_sender_sp(f, caller.unextended_sp()); + ContinuationHelper::InterpretedFrame::patch_sender_sp(f, caller); } assert(!bottom || !_cont.is_empty() || Continuation::is_continuation_entry_frame(f, nullptr), ""); @@ -2030,9 +2051,9 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c frame f = new_stack_frame(hf, caller, is_bottom_frame); - intptr_t* const stack_frame_top = f.sp(); + intptr_t* const stack_frame_top = f.sp() + frame::metadata_words_at_top; intptr_t* const stack_frame_bottom = ContinuationHelper::InterpretedFrame::frame_bottom(f); - intptr_t* const heap_frame_top = hf.unextended_sp(); + intptr_t* const heap_frame_top = hf.unextended_sp() + frame::metadata_words_at_top; intptr_t* const heap_frame_bottom = ContinuationHelper::InterpretedFrame::frame_bottom(hf); assert(hf.is_heap_frame(), "should be"); @@ -2041,7 +2062,7 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c assert((stack_frame_bottom >= stack_frame_top + fsize) && (stack_frame_bottom <= stack_frame_top + fsize + 1), ""); // internal alignment on aarch64 - // on AArch64 we add padding between the locals and the rest of the frame to keep the fp 16-byte-aligned + // on AArch64/PPC64 we add padding between the locals and the rest of the frame to keep the fp 16-byte-aligned const int locals = hf.interpreter_frame_method()->max_locals(); assert(hf.is_heap_frame(), "should be"); assert(!f.is_heap_frame(), "should not be"); @@ -2101,9 +2122,10 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n int fsize = ContinuationHelper::CompiledFrame::size(hf) + added_argsize; assert(fsize <= (int)(caller.unextended_sp() - f.unextended_sp()), ""); - intptr_t* from = heap_frame_top - frame::metadata_words; - intptr_t* to = stack_frame_top - frame::metadata_words; - int sz = fsize + frame::metadata_words; + intptr_t* from = heap_frame_top - frame::metadata_words_at_bottom; + intptr_t* to = stack_frame_top - frame::metadata_words_at_bottom; + // copy metadata, except the metadata at the top of the (unextended) entry frame + int sz = fsize + frame::metadata_words_at_bottom + (is_bottom_frame && added_argsize == 0 ? 0 : frame::metadata_words_at_top); // If we're the bottom-most thawed frame, we're writing to within one word from entrySP // (we might have one padding word for alignment) @@ -2137,7 +2159,7 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n // can only fix caller once this frame is thawed (due to callee saved regs); this happens on the stack _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance); } else if (_cont.tail()->has_bitmap() && added_argsize > 0) { - clear_bitmap_bits(heap_frame_top + ContinuationHelper::CompiledFrame::size(hf), added_argsize); + clear_bitmap_bits(heap_frame_top + ContinuationHelper::CompiledFrame::size(hf) + frame::metadata_words_at_top, added_argsize); } DEBUG_ONLY(after_thaw_java_frame(f, is_bottom_frame);) @@ -2210,9 +2232,9 @@ void ThawBase::finish_thaw(frame& f) { } assert(chunk->is_empty() == (chunk->max_thawing_size() == 0), ""); - if ((intptr_t)f.sp() % frame::frame_alignment != 0) { + if (!is_aligned(f.sp(), frame::frame_alignment)) { assert(f.is_interpreted_frame(), ""); - f.set_sp(f.sp() - 1); + f.set_sp(align_down(f.sp(), frame::frame_alignment)); } push_return_frame(f); chunk->fix_thawed_frame(f, SmallRegisterMap::instance); // can only fix caller after push_return_frame (due to callee saved regs) @@ -2240,7 +2262,7 @@ void ThawBase::push_return_frame(frame& f) { // see generate_cont_thaw f.print_value_on(&ls, nullptr); } - assert(f.sp() - frame::metadata_words >= _top_stack_address, "overwrote past thawing space" + assert(f.sp() - frame::metadata_words_at_bottom >= _top_stack_address, "overwrote past thawing space" " to: " INTPTR_FORMAT " top_address: " INTPTR_FORMAT, p2i(f.sp() - frame::metadata_words), p2i(_top_stack_address)); ContinuationHelper::Frame::patch_pc(f, f.raw_pc()); // in case we want to deopt the frame in a full transition, this is checked. ContinuationHelper::push_pd(f); diff --git a/src/hotspot/share/runtime/continuationHelper.hpp b/src/hotspot/share/runtime/continuationHelper.hpp index 9d95fc56f0df7..ed699a36802ce 100644 --- a/src/hotspot/share/runtime/continuationHelper.hpp +++ b/src/hotspot/share/runtime/continuationHelper.hpp @@ -95,7 +95,7 @@ class ContinuationHelper::InterpretedFrame : public ContinuationHelper::Frame { static inline address* return_pc_address(const frame& f); static address return_pc(const frame& f); - static void patch_sender_sp(frame& f, intptr_t* sp); + static void patch_sender_sp(frame& f, const frame& caller); static int size(const frame& f, InterpreterOopMap* mask); static int size(const frame& f); diff --git a/src/hotspot/share/runtime/continuationWrapper.inline.hpp b/src/hotspot/share/runtime/continuationWrapper.inline.hpp index 3e8fbfa52dfa9..65015b08352c2 100644 --- a/src/hotspot/share/runtime/continuationWrapper.inline.hpp +++ b/src/hotspot/share/runtime/continuationWrapper.inline.hpp @@ -125,6 +125,11 @@ class ContinuationWrapper : public StackObj { intptr_t* entryFP() const { return _entry->entry_fp(); } address entryPC() const { return _entry->entry_pc(); } int argsize() const { assert(_entry->argsize() >= 0, ""); return _entry->argsize(); } + int entry_frame_extension() const { + // the entry frame is extended if the bottom frame has stack arguments + assert(_entry->argsize() >= 0, ""); + return _entry->argsize() == 0 ? _entry->argsize() : _entry->argsize() + frame::metadata_words_at_top; + } void set_argsize(int value) { _entry->set_argsize(value); } bool is_empty() const { return last_nonempty_chunk() == nullptr; } diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 6d9170e633003..66c5124799394 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -1629,7 +1629,14 @@ void FrameValues::print_on(outputStream* st, int min_index, int max_index, intpt } else { if (on_heap && *fv.location != 0 && *fv.location > -100 && *fv.location < 100 - && (strncmp(fv.description, "interpreter_frame_", 18) == 0 || strstr(fv.description, " method "))) { +#if !defined(PPC64) + && (strncmp(fv.description, "interpreter_frame_", 18) == 0 || strstr(fv.description, " method ")) +#else // !defined(PPC64) + && (strcmp(fv.description, "sender_sp") == 0 || strcmp(fv.description, "top_frame_sp") == 0 || + strcmp(fv.description, "esp") == 0 || strcmp(fv.description, "monitors") == 0 || + strcmp(fv.description, "locals") == 0 || strstr(fv.description, " method ")) +#endif //!defined(PPC64) + ) { st->print_cr(" " INTPTR_FORMAT ": %18d %s", p2i(fv.location), (int)*fv.location, fv.description); } else { st->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT " %s", p2i(fv.location), *fv.location, fv.description); diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index e2f0b40f4994c..413a5830dbce2 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -3120,17 +3120,21 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) { CodeBuffer buffer(buf); if (method->is_continuation_enter_intrinsic()) { - buffer.initialize_stubs_size(128); + buffer.initialize_stubs_size(128 PPC64_ONLY(+ 32)); } struct { double data[20]; } locs_buf; struct { double data[20]; } stubs_locs_buf; buffer.insts()->initialize_shared_locs((relocInfo*)&locs_buf, sizeof(locs_buf) / sizeof(relocInfo)); -#if defined(AARCH64) +#if defined(AARCH64) || defined(PPC64) // On AArch64 with ZGC and nmethod entry barriers, we need all oops to be // in the constant pool to ensure ordering between the barrier and oops // accesses. For native_wrappers we need a constant. - buffer.initialize_consts_size(8); + // On PPC64 the continuation enter intrinsic needs the constant pool for the compiled + // static java call that is resolved in the runtime. + if (PPC64_ONLY(method->is_continuation_enter_intrinsic() &&) true) { + buffer.initialize_consts_size(8 PPC64_ONLY(+ 24)); + } #endif buffer.stubs()->initialize_shared_locs((relocInfo*)&stubs_locs_buf, sizeof(stubs_locs_buf) / sizeof(relocInfo)); MacroAssembler _masm(&buffer); diff --git a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp index 36f81f62f4542..1c24a9ed28c7f 100644 --- a/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp +++ b/src/hotspot/share/runtime/stackChunkFrameStream.inline.hpp @@ -123,10 +123,60 @@ inline bool StackChunkFrameStream::is_interpreted() c return false; } +// StackChunkFrameStream::frame_size() returns the words required to +// store the given frame as the only frame in a StackChunk. This is the size of the +// frame itself plus its stack arguments plus metadata at the caller's frame top (1) +// +// |====================| --- +// | F0's stackargs | ^ +// | | | +// |--------------------| | +// | metadata@top | <- caller's sp +// |====================| | +// | metadata@bottom(2) | | +// |--------------------| +// | | size S0 +// | Frame F0 | --- |====================| --- +// | | | ^ | F1's stackargs | ^ +// | | | | | | | +// |--------------------| | overlap |--------------------| | +// | metadata@top(1) |<- sp v v | metadata@top | <- caller's sp +// |====================| --- --- |====================| | +// | metadata@bottom | | +// | |--------------------| +// | | Frame F1 | size S1 +// Stack Growth | (F0's callee) | +// | | | | +// | | | | +// v |--------------------| | +// | metadata@top |<- sp v +// |====================| --- +// +// 2 frames of the same kind (interpreted or compiled) overlap. So the total +// size required in the StackChunk is S0 + S1 - overlap, where the overlap is +// the size of F1's stackargs plus frame::metadata_words_at_top. +// +// The callers of frame_size() are supposed to deduct the overlap. The bottom +// frame in the StackChunk obviously does not overlap with it's caller, as it is +// in the parent chunk. +// +// There is no overlap if caller/callee are of different kinds. In that case the +// caller is extended to accomodate the callee's stack arguments. The extension +// is not counted though in the caller's size, so there is indeed no overlap. +// +// See ppc implementation of StackChunkFrameStream::interpreter_frame_size() +// for more details. +// +// (1) Metadata at frame top (see frame::metadata_words_at_top) +// Part of the overlap. Used on ppc64, empty on x86_64, aarch64 +// (2) Metadata at the frame bottom (see frame::metadata_words_at_bottom) +// Not part of the overlap. +// Used on x86_64 (saved rbp, ret. addr.), aarch64. Empty on ppc64. +// template inline int StackChunkFrameStream::frame_size() const { return is_interpreted() ? interpreter_frame_size() - : cb()->frame_size() + stack_argsize(); + : cb()->frame_size() + stack_argsize() + frame::metadata_words_at_top; } template diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java new file mode 100644 index 0000000000000..629007ac83441 --- /dev/null +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java @@ -0,0 +1,976 @@ +/* + * Copyright (c) 2022 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/** + * @test id=all-policies-no-system-gc + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 0 0 + */ + +/** + * @test id=policy-1-gc-after-yield + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 1 1 + */ + +/** + * @test id=policy-2-gc-after-yield + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 2 1 + */ + +/** + * @test id=policy-3-gc-after-yield + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 3 1 + */ + +/** + * @test id=policy-4-gc-after-yield + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 4 1 + */ + +/** + * @test id=policy-5-gc-after-yield + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) + * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 5 1 + */ + +import java.lang.reflect.Method; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import jdk.internal.vm.Continuation; +import jdk.internal.vm.ContinuationScope; +import static jdk.test.lib.Asserts.*; + +import compiler.whitebox.CompilerWhiteBoxTest; +import jdk.test.whitebox.WhiteBox; + +public class BasicExp { + static final ContinuationScope THE_SCOPE = new ContinuationScope() {}; + + public static final Pattern COMP_NONE = Pattern.compile("COMP_NONE"); + public static final Pattern COMP_ALL = Pattern.compile("COMP_ALL"); + public static final Pattern CONT_METHS = Pattern.compile("^(enter|enter0|yield|yield0)$"); + + public static int compPolicySelection; + public static boolean callSystemGC; + public static int compLevel; + + public static final WhiteBox WB = WhiteBox.getWhiteBox(); + + enum TestCaseVariants { + NO_VARIANT, + // Exception + THROW_HANDLED_EXCEPTION, + THROW_UNHANDLED_EXCEPTION, + // Synchronization + ALLOC_MONITOR, + // There are values on the expression stack that are not call parameters + EXPR_STACK_NOT_EMPTY, + } + + public static class HandledException extends Exception { } + public static class UnhandledException extends Error { } + + public static void main(String[] args) { + try { + // Run tests with C2 compilations + compLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; + // // Run tests with C1 compilations + // compLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE; + + compPolicySelection = Integer.parseInt(args[0]); + callSystemGC = Integer.parseInt(args[1]) == 1; + runTests(); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public static void runTests() { + // TODO: enable + System.out.println("$$$0 Running test cases with the following settings:"); + System.out.println("compLevel=" + compLevel); + System.out.println("callSystemGC=" + callSystemGC); + System.out.println(); + + WB.deoptimizeAll(); + + boolean all = compPolicySelection == 0; + if (compPolicySelection == 1 || all) runTests(new CompilationPolicy(7 /*warmup*/, COMP_NONE, COMP_NONE /*Cont. pattern*/)); + if (compPolicySelection == 2 || all) runTests(new CompilationPolicy(7 /*warmup*/, 1 /* length comp. window */)); + if (compPolicySelection == 3 || all) runTests(new CompilationPolicy(7 /*warmup*/, 2 /* length comp. window */)); + if (compPolicySelection == 4 || all) runTests(new CompilationPolicy(7 /*warmup*/, 3 /* length comp. window */)); + if (compPolicySelection == 5 || all) runTests(new CompilationPolicy(7 /*warmup*/, COMP_ALL, CONT_METHS /*Cont. pattern*/)); + if (compPolicySelection >= 6) throw new Error("CompilationPolicy with number " + compPolicySelection + " does not exist"); + } + + public static void runTests(CompilationPolicy compPolicy) { + System.out.println("$$$1 Running test cases with the following policy:"); + compPolicy.print(); System.out.println(); + + new ContinuationRunYieldRunTest().runTestCase(3, compPolicy); + new Continuation3Frames(TestCaseVariants.NO_VARIANT).runTestCase(4, compPolicy); + new Continuation3Frames(TestCaseVariants.THROW_HANDLED_EXCEPTION).runTestCase(4, compPolicy); + new Continuation3Frames(TestCaseVariants.THROW_UNHANDLED_EXCEPTION).runTestCase(4, compPolicy); + new Continuation3Frames(TestCaseVariants.ALLOC_MONITOR).runTestCase(4, compPolicy); + new Continuation3Frames(TestCaseVariants.EXPR_STACK_NOT_EMPTY).runTestCase(4, compPolicy); + new ContinuationRunYieldRunTest().runTestCase( 1, compPolicy); + new ContinuationYieldEnlargeStackYield().runTestCase(1, compPolicy); + new ContinuationYieldReduceStackYield().runTestCase(1, compPolicy); + new ContinuationCompiledFramesWithStackArgs_3c0().runTestCase(1, compPolicy); + new ContinuationCompiledFramesWithStackArgs_3c4().runTestCase(1, compPolicy); + new ContinuationCompiledFramesWithStackArgs().runTestCase(1, compPolicy); + new ContinuationDeepRecursion().runTestCase(3, compPolicy); + new ContinuationDeepRecursionStackargs().runTestCase(3, compPolicy); + } + + public static class CompilationPolicy { + public int warmupIterations; + public Pattern methodPattern; + public Pattern contMethPattern; + + public CompWindowMode compWindowMode; + public int winPos; + public int winLen; + + public Method[] methods; + + public enum CompWindowMode { + NO_COMP_WINDOW, COMP_WINDOW, DEOPT_WINDOW + } + + public CompilationPolicy(int warmupIterations, Pattern methodPattern, Pattern contMethPattern) { + this(warmupIterations, 0, methodPattern, contMethPattern, CompWindowMode.NO_COMP_WINDOW); + } + + public CompilationPolicy(int warmupIterations, int windowLength, Pattern methodPattern, Pattern contMethPattern) { + this(warmupIterations, windowLength, methodPattern, contMethPattern, CompWindowMode.COMP_WINDOW); + } + + public CompilationPolicy(int warmupIterations, int windowLength, Pattern methodPattern, Pattern contMethPattern, + CompWindowMode startMode) { + this.warmupIterations = warmupIterations; + this.methodPattern = methodPattern; + this.contMethPattern = contMethPattern; + this.winPos = 0; + this.winLen = windowLength; + this.compWindowMode = startMode; + } + + public CompilationPolicy(int warmupIterations, int windowLength) { + this(warmupIterations, windowLength, COMP_ALL, CONT_METHS); + } + + public int warmupIterations() { + return this.warmupIterations; + } + + public boolean compileMethods() { + boolean newCompilation = false; + log("@@ Compiling test methods according to compilation policy"); + print(); + for (int i = 0; i < methods.length; i++) { + Method meth = methods[i]; + boolean inWindow = i >= winPos && i < (winPos+winLen); + boolean shouldBeCompiled = compWindowMode == CompWindowMode.NO_COMP_WINDOW + || (inWindow && compWindowMode == CompWindowMode.COMP_WINDOW) + || (!inWindow && compWindowMode == CompWindowMode.DEOPT_WINDOW); + boolean isCompiled = WB.isMethodCompiled(meth); + log("methods["+i+"] inWindow="+inWindow + " isCompiled="+isCompiled+" shouldBeCompiled="+shouldBeCompiled+" method=`"+meth+"`"); + if (isCompiled != shouldBeCompiled) { + if (shouldBeCompiled) { + log(" Compiling methods["+i+"]"); + enqForCompilation(meth); + newCompilation = true; + assertTrue(WB.isMethodCompiled(meth), "Run with -Xbatch"); + } else { + assertFalse(WB.isMethodQueuedForCompilation(meth), "Run with -Xbatch"); + log(" Deoptimizing methods["+i+"]"); + WB.deoptimizeMethod(meth); + } + } + } + return newCompilation; + } + + @SuppressWarnings("deprecation") + public boolean enqForCompilation(Method meth) { + return WB.enqueueMethodForCompilation(meth, compLevel); + } + + public void log(String m) { + System.out.println(m); + } + + public void print() { + log("warmupIterations=" + warmupIterations); + log("methodPattern=" + methodPattern); + log("continuationMethPattern=" + contMethPattern); + log("compWindowMode=" + compWindowMode); + log("winLen=" + winLen); + } + + public void setMethods(Method[] methods) { + this.methods = methods; + if (compWindowMode == CompWindowMode.NO_COMP_WINDOW) { + winLen = methods.length; + } + } + + public boolean shiftWindow() { + if(compWindowMode == CompWindowMode.NO_COMP_WINDOW) return false; + if (++winPos == methods.length) { + winPos = 0; + if (compWindowMode == CompWindowMode.DEOPT_WINDOW) { + compWindowMode = CompWindowMode.COMP_WINDOW; + return false; // we're done + } + compWindowMode = CompWindowMode.DEOPT_WINDOW; + } + return true; // continue + } + } + + /** + * Base class for test cases + */ + public static abstract class TestCaseBase implements Runnable { + public int yieldCalls; + public int warmUpCount; + public CompilationPolicy compPolicy; + public final TestCaseVariants testVariant; + + public TestCaseBase() { + testVariant = TestCaseVariants.NO_VARIANT; + } + + public TestCaseBase(TestCaseVariants excBehav) { + this.testVariant = excBehav; + } + + public void log_dontjit() { + System.out.println(); + } + + public void log_dontjit(String m) { + if (warmUpCount > 0) { + System.out.print("[" + warmUpCount + "] "); + } + System.out.println(m); + } + + public void runTestCase(int yieldCalls, CompilationPolicy compPolicy) { + this.yieldCalls = yieldCalls; + log_dontjit(">>>> Executing test case " + getClass().getName() + " (yieldCalls=" + yieldCalls + ", " + "testVariant=" + testVariant + ")"); + init(compPolicy); + try { + log_dontjit("Warm-up test case"); + setup_dontjit(true /* for warmup */); + for(warmUpCount = 1; warmUpCount <= compPolicy.warmupIterations(); warmUpCount++) { + testEntry_dontinline(); + } + warmUpCount = 0; + log_dontjit("Warm-up test case DONE"); + + setup_dontjit(false /* for warmup */); + do { + compPolicy.compileMethods(); + do { + log_dontjit("Running test case (Reresolve Call Sites)"); + testEntry_dontinline(); + log_dontjit("Running test case DONE (Reresolve Call Sites)"); + } while(compPolicy.compileMethods()); + + log_dontjit("Running test case BEGIN"); + testEntry_dontinline(); + log_dontjit("Running test case DONE"); + } while(compPolicy.shiftWindow()); + } finally { + log_dontjit("<<<< Finished test case " + getClass().getName()); log_dontjit(); + } + } + + public void setup_dontjit(boolean warmup) { + } + + public void init(CompilationPolicy compPolicy) { + this.compPolicy = compPolicy; + ArrayList selectedMethods = new ArrayList(); + Pattern p = compPolicy.methodPattern; + if (p != COMP_NONE) { + Class c = getClass(); + Method methods[] = c.getDeclaredMethods(); + for (Method meth : methods) { + if (p == COMP_ALL || p.matcher(meth.getName()).matches()) { + if (!meth.getName().contains("dontjit")) { + selectedMethods.add(meth); + } + } + } + } + + p = compPolicy.contMethPattern; + if (compPolicy.contMethPattern != COMP_NONE) { + Class c = Continuation.class; + Method methods[] = c .getDeclaredMethods(); + for (Method meth : methods) { + if (p.matcher(meth.getName()).matches()) { + selectedMethods.add(meth); + } + } + } + // Sort in caller/callee order + selectedMethods.sort(new Comparator() { + @Override + public int compare(Method m1, Method m2) { + String n1 = m1.getName(); + String n2 = m2.getName(); + // log_dontjit("n1=" + n1 + " n2=" + n2); + int p1 = -1; + int p2 = -1; + int i = n1.indexOf("ord"); + if (i >= 0) { + p1 = Integer.parseInt(n1.substring(i+3, i+6)); + } + i = n2.indexOf("ord"); + if (i >= 0) { + p2 = Integer.parseInt(n2.substring(i+3, i+6)); + } + if (p1 < 0) p1 = getScoreKnownMethods(n1); + if (p2 < 0) p2 = getScoreKnownMethods(n2); + assertFalse(p1 == -1 || p2 == -1, "Cannot compare " + n1 + " with " + n2); + return p1 - p2; + } + + private int getScoreKnownMethods(String n) { + int p = -1; + if (n.equals("enter")) p = 20; // Continuation.enter + if (n.equals("enter0")) p = 30; // Continuation.enter0 + if (n.equals("run")) p = 50; // Called by Continuation.enter0 + if (n.equals("yield")) p = 1000; // caller of yield0 + if (n.equals("yield0")) p = 2000; // top frame + return p; + } + }); + compPolicy.setMethods(selectedMethods.toArray(new Method[selectedMethods.size()])); + } + + public void testEntry_dontinline() { + Continuation cont = new Continuation(THE_SCOPE, this); + do { + try { + cont.run(); + } catch(UnhandledException e) { + log_dontjit("Exc: " + e); + } + if (callSystemGC) System.gc(); + checkFrames_dontjit(cont); + } while (!cont.isDone()); + } + + public void checkFrames_dontjit(Continuation cont) { + } // Override in subclass as appropriate + + @Override + public void run() { + fail("Should not call TestCaseBase::run"); + } + + public void sleep(Duration d) { + try { Thread.sleep(d); } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + + static final long i1=1; static final long i2=2; static final long i3=3; + static final long i4=4; static final long i5=5; static final long i6=6; static final long i7=7; + static final long i8=8; static final long i9=9; static final long i10=10; static final long i11=11; static final long i12=12; + static final long i13=13; static final long i14=14; static final long i15=15; static final long i16=16; + } + + /** + * Trivial run/yield/run test + */ + public static class ContinuationRunYieldRunTest extends TestCaseBase { + public String sField; + + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + for(int i = 0; i < yieldCalls; i++) { + log_dontjit("Yield #" + i); + String s1 = "str1"; + Continuation.yield(THE_SCOPE); + String s2 = s1+"str2"; + sField = s2; + } + } + } + + /** + * Yield, make continuation (stack) larger, yield again. + */ + public static class ContinuationYieldEnlargeStackYield extends TestCaseBase { + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + Continuation.yield(THE_SCOPE); + log_dontjit("Back from 1st yield. Now call a method to make the stack larger."); + ord101_callYieldWithLargerStackAgain_dontinline(); + } + + private void ord101_callYieldWithLargerStackAgain_dontinline() { + log_dontjit("Now there's a new frame on stack. Call yield again."); + Continuation.yield(THE_SCOPE); + log_dontjit("Back again after 2nd yield."); + } + } + + + /** + * Yield, make continuation (stack) larger, yield again. + */ + public static class ContinuationYieldReduceStackYield extends TestCaseBase { + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + ord101_methodWithFirstYield_dontinline(); + log_dontjit("The frame of ord101_methodWithFirstYield_dontinline has been removed now. Call yield again."); + Continuation.yield(THE_SCOPE); + log_dontjit("Back again after 2nd yield."); + } + + public void ord101_methodWithFirstYield_dontinline() { + Continuation.yield(THE_SCOPE); + log_dontjit("Back from 1st yield. Now return to reduce stack size."); + } + } + + /** + * Freeze/thaw 3 compiled frames. + */ + public static class Continuation3Frames extends TestCaseBase { + public int yieldCount; + public long resLong; + public volatile String putOnExprStack; + + public Continuation3Frames(TestCaseVariants excBehav) { + super(excBehav); + } + + @Override + public void run() { + for(int i = 0; i < yieldCalls; i++) { + Throwable caughtException = null; + putOnExprStack = "exprStckVal "; + resLong = 0; + try { + String s1 = "str1"; + String result = ord101_testMethod_dontinline(i1, i2, i3, s1); + assertEQ(resLong, testVariant == TestCaseVariants.ALLOC_MONITOR ? 7L : 6L); + assertEQ(result, testVariant == TestCaseVariants.EXPR_STACK_NOT_EMPTY ? "exprStckVal str1str2str3" : "str1str2str3"); + } catch (HandledException e) { + caughtException = e; + } + assertTrue(testVariant != TestCaseVariants.THROW_HANDLED_EXCEPTION || (caughtException instanceof HandledException), + "Exception handling error"); + } + } + + public String ord101_testMethod_dontinline(long a1, long a2, long a3, String s1) throws HandledException { + String s2 = s1+"str2"; + return ord102_testMethod_dontinline(a1, a2, a3, s2); + } + + public String ord102_testMethod_dontinline(long a1, long a2, long a3, String s2) throws HandledException { + if (testVariant == TestCaseVariants.ALLOC_MONITOR) { + synchronized (this) { + resLong++; + } + } + if (testVariant == TestCaseVariants.EXPR_STACK_NOT_EMPTY) { + return putOnExprStack_testMethod_dontjit_dontinline() + + ord103_testMethod_dontinline(a1, a2, a3, s2); + } else { + return ord103_testMethod_dontinline(a1, a2, a3, s2); + } + } + + public String ord103_testMethod_dontinline(long a1, long a2, long a3, String s2) throws HandledException { + return ord104_testMethod_dontinline(a1, a2, a3, s2); + } + + public String ord104_testMethod_dontinline(long a1, long a2, long a3, String s2) throws HandledException { + long res = a2; + String s3 = s2+"str3"; + log_dontjit("Yield #" + yieldCount++); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount); + if (testVariant == TestCaseVariants.THROW_HANDLED_EXCEPTION) { + log_dontjit("Exc: throw handled"); + throw new HandledException(); + } + if (testVariant == TestCaseVariants.THROW_UNHANDLED_EXCEPTION) { + log_dontjit("Exc: throw unhandled"); + throw new UnhandledException(); + } + resLong += res+a1+a3; + return s3; + } + + public String putOnExprStack_testMethod_dontjit_dontinline() { + return putOnExprStack; + } + + @Override + public void checkFrames_dontjit(Continuation cont) { + List frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); + assertEquals(frames, cont.isDone() ? List.of() + : Arrays.asList("yield", "ord104_testMethod_dontinline", "ord103_testMethod_dontinline", + "ord102_testMethod_dontinline", "ord101_testMethod_dontinline", "run", "enter0", "enter")); + } + } + + /** + * Deep recursion to exercise fast freezing into non-empty chunk + */ + public static class ContinuationDeepRecursion extends TestCaseBase { + public int limit; + public int yield1_depth; + public int yield2_depth; + + @Override + public void setup_dontjit(boolean warmup) { + if (warmup) { + limit = 10; + yield1_depth = 7; + yield2_depth = 3; + } else { + limit = 100; + yield1_depth = 70; + yield2_depth = 60; + } + } + + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + ord101_recurse_dontinline(0); + } + + public void ord101_recurse_dontinline(int depth) { + if (depth >= limit) { + log_dontjit("yield at depth " + depth); + ord102_yield_dontinline(0); + log_dontjit("After yield at depth " + depth); + return; + } + ord101_recurse_dontinline(depth + 1); + if (depth == yield1_depth || depth == yield2_depth) { + log_dontjit("yield at depth " + depth); + ord102_yield_dontinline(0); + log_dontjit("After yield at depth " + depth); + } + } + + // Add a few frames before yield + public void ord102_yield_dontinline(int depth) { + if (depth >= 2) { + Continuation.yield(THE_SCOPE); + return; + } + ord102_yield_dontinline(depth + 1); + } + } + + /** + * Deep recursion to exercise fast freezing into non-empty chunk. + * nmethods have stack arguments. + */ + public static class ContinuationDeepRecursionStackargs extends TestCaseBase { + public int limit; + public int yield1_depth; + public int yield2_depth; + + @Override + public void setup_dontjit(boolean warmup) { + if (warmup) { + limit = 10; + yield1_depth = 7; + yield2_depth = 3; + } else { + limit = 100; + yield1_depth = 70; + yield2_depth = 60; + } + } + + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + long res = ord101_recurse_dontinline(0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11); + if (res != i1+i2+i3+i4+i5+i6+i7+i8+i9+i10+i11) { + throw new Error(); + } + } + + public long ord101_recurse_dontinline(int depth, long l1, long l2, long l3, long l4, long l5, long l6, long l7, long l8, long l9, long l10, long l11) { + if (depth >= limit) { + log_dontjit("yield at depth " + depth); + ord102_yield_dontinline(0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); + log_dontjit("After yield at depth " + depth); + return l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11; + } + long res = ord101_recurse_dontinline(depth + 1, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); + if (res != l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11) { + throw new Error(); + } + if (depth == yield1_depth || depth == yield2_depth) { + log_dontjit("yield at depth " + depth); + long res1 = ord102_yield_dontinline(0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); + if (res1 != l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11) { + throw new Error(); + } + log_dontjit("After yield at depth " + depth); + } + return res; + } + + // Add a few frames before yield + public long ord102_yield_dontinline(int depth, long l1, long l2, long l3, long l4, long l5, long l6, long l7, long l8, long l9, long l10, long l11) { + if (depth >= 2) { + Continuation.yield(THE_SCOPE); + return l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11; + } + long res = ord102_yield_dontinline(depth + 1, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); + if (res != l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11) { + throw new Error(); + } + return res; + } + } + + /** + * Freeze/thaw compiled frame with a few stack arguments + * icj is a call with i incoming stack parameters and j outgoing stack parameters. + */ + public static class ContinuationCompiledFramesWithStackArgs_3c0 extends TestCaseBase { + public int yieldCount; + + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + yieldCount = 0; + long result = ord101_testMethod_dontinline(); + assertEQ(result, 136L); + } + + public long ord101_testMethod_dontinline() { + long res = ord102_testMethod_dontinline(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11); + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord108_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord108_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord102_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, + long a9, long a10, long a11) { + long res = a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+i12+i13+i14+i15+i16; + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord109_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord109_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + } + + /** + * Freeze/thaw compiled frame with a few stack arguments, incoming _and_ outgoing + * icj is a call with i incoming stack parameters and j outgoing stack parameters. + */ + public static class ContinuationCompiledFramesWithStackArgs_3c4 extends TestCaseBase { + public int yieldCount; + + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + yieldCount = 0; + long result = ord101_testMethod_dontinline(); + assertEQ(result, 136L); + } + + public long ord101_testMethod_dontinline() { + long res = ord102_testMethod_dontinline(i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14); + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord108_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord108_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord102_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, + long a9, long a10, long a11, long a12, long a13, long a14) { + long res = ord103_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, i15); + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord109_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord109_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord103_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, + long a8, long a9, long a10, long a11, long a12, long a13, long a14, long a15) { + long res = a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15+i16; + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord109_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord109_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + } + + /** + * Freeze/thaw compiled frame with many stack arguments + */ + public static class ContinuationCompiledFramesWithStackArgs extends TestCaseBase { + public int yieldCount; + + @Override + public void run() { + log_dontjit("Continuation running on thread " + Thread.currentThread()); + yieldCount = 0; + long result = ord101_testMethod_dontinline(i1); + assertEQ(result, 136L); + } + + public long ord101_testMethod_dontinline(long a1) { + long res = ord102_testMethod_dontinline(a1, i2); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord102_testMethod_dontinline(long a1, long a2) { + long res = ord103_testMethod_dontinline(a1, a2, i3); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord103_testMethod_dontinline(long a1, long a2, long a3) { + long res = ord104_testMethod_dontinline(a1, a2, a3, i4); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord104_testMethod_dontinline(long a1, long a2, long a3, long a4) { + long res = ord105_testMethod_dontinline(a1, a2, a3, a4, i5); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord105_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5) { + long res = ord106_testMethod_dontinline(a1, a2, a3, a4, a5, i6); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord106_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6) { + long res = ord107_testMethod_dontinline(a1, a2, a3, a4, a5, a6, i7); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord107_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7) { + long res = ord108_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, i8); + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord108_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord108_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord108_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { + long res = ord109_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, i9); + log_dontjit("Yield #" + yieldCount); + log_dontjit("ord109_testMethod_dontinline res=" + res); + Continuation.yield(THE_SCOPE); + log_dontjit("a/y ord109_testMethod_dontinline res=" + res); + log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord109_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { + long res = ord110_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, i10); + Continuation.yield(THE_SCOPE); + return res; + } + + public long ord110_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) { + long res = ord111_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, i11); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord111_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11) { + long res = ord112_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, i12); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord112_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12) { + long res = ord113_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, i13); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord113_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13) { + long res = ord114_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, i14); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord114_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13, long a14) { + long res = ord115_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, i15); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord115_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13, long a14, long a15) { + long res = ord116_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, i16); + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + return res; + } + + public long ord116_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13, long a14, long a15, long a16) { + long res = a2+a4+a6+a8+a10+a12+a14+a16; + log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + res += a1+a3+a5+a7+a9+a11+a13+a15; + return res; + } + } +} From 0d12b0577d8314ed1d571d9cac65fa8866939180 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Fri, 4 Nov 2022 10:04:00 +0100 Subject: [PATCH 02/11] Use callers_sp for fsize calculation in recurse_freeze_interpreted_frame --- .../cpu/aarch64/continuationHelper_aarch64.inline.hpp | 4 ++++ src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp | 5 +++++ src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp | 4 ++++ src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp | 4 ++++ src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp | 5 +++++ src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp | 4 ++++ src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp | 5 +++++ src/hotspot/share/runtime/continuationFreezeThaw.cpp | 3 ++- src/hotspot/share/runtime/continuationHelper.hpp | 2 +- 9 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp index 3b070c00359a5..07a5b6d3a2965 100644 --- a/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/continuationHelper_aarch64.inline.hpp @@ -138,4 +138,8 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return f.unextended_sp() + (callee_interpreted ? callee_argsize : 0); } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + return f.fp() + frame::metadata_words; +} + #endif // CPU_AARCH64_CONTINUATIONHELPER_AARCH64_INLINE_HPP diff --git a/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp b/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp index 4a9a7fe8e57cb..a75fccdbc7d9f 100644 --- a/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp +++ b/src/hotspot/cpu/arm/continuationHelper_arm.inline.hpp @@ -122,4 +122,9 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return NULL; } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + Unimplemented(); + return NULL; +} + #endif // CPU_ARM_CONTINUATIONHELPER_ARM_INLINE_HPP diff --git a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp index 0fbd1a4f318d7..4a226d281ae7d 100644 --- a/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationHelper_ppc.inline.hpp @@ -178,4 +178,8 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return pseudo_unextended_sp + (callee_interpreted ? callee_argsize_incl_metadata : 0); } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + return f.fp(); +} + #endif // CPU_PPC_CONTINUATIONFRAMEHELPERS_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp index 28ff3d40375f6..79fc373007be9 100644 --- a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp @@ -120,4 +120,8 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return NULL; } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + return f.fp(); +} + #endif // CPU_RISCV_CONTINUATIONFRAMEHELPERS_RISCV_INLINE_HPP diff --git a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp index 0a00cb69ad234..6c1a152339f2c 100644 --- a/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp +++ b/src/hotspot/cpu/s390/continuationHelper_s390.inline.hpp @@ -122,4 +122,9 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return NULL; } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + Unimplemented(); + return NULL; +} + #endif // CPU_S390_CONTINUATIONHELPER_S390_INLINE_HPP diff --git a/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp b/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp index 39184d2e3c703..e1c24cb5d5385 100644 --- a/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp +++ b/src/hotspot/cpu/x86/continuationHelper_x86.inline.hpp @@ -137,4 +137,8 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return f.unextended_sp() + (callee_interpreted ? callee_argsize : 0); } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + return f.fp() + frame::metadata_words; +} + #endif // CPU_X86_CONTINUATIONHELPER_X86_INLINE_HPP diff --git a/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp b/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp index a7e3119ca2da1..70a12473e0eb0 100644 --- a/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp +++ b/src/hotspot/cpu/zero/continuationHelper_zero.inline.hpp @@ -120,4 +120,9 @@ inline intptr_t* ContinuationHelper::InterpretedFrame::frame_top(const frame& f, return NULL; } +inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) { + Unimplemented(); + return NULL; +} + #endif // CPU_ZERO_CONTINUATIONHELPER_ZERO_INLINE_HPP diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index f1d82bee7cd82..257c088b95b83 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -1055,8 +1055,9 @@ NOINLINE freeze_result FreezeBase::recurse_freeze_interpreted_frame(frame& f, fr // The frame's top never includes the stack arguments to the callee intptr_t* const stack_frame_top = ContinuationHelper::InterpretedFrame::frame_top(f, callee_argsize, callee_interpreted); + intptr_t* const callers_sp = ContinuationHelper::InterpretedFrame::callers_sp(f); const int locals = f.interpreter_frame_method()->max_locals(); - const int fsize = f.fp() + frame::metadata_words + locals - stack_frame_top; + const int fsize = callers_sp + frame::metadata_words_at_top + locals - stack_frame_top; intptr_t* const stack_frame_bottom = ContinuationHelper::InterpretedFrame::frame_bottom(f); assert(stack_frame_bottom - stack_frame_top >= fsize, ""); // == on x86 diff --git a/src/hotspot/share/runtime/continuationHelper.hpp b/src/hotspot/share/runtime/continuationHelper.hpp index ed699a36802ce..1ab101709aafd 100644 --- a/src/hotspot/share/runtime/continuationHelper.hpp +++ b/src/hotspot/share/runtime/continuationHelper.hpp @@ -90,7 +90,7 @@ class ContinuationHelper::InterpretedFrame : public ContinuationHelper::Frame { static inline intptr_t* frame_top(const frame& f); static inline intptr_t* frame_top(const frame& f, int callee_argsize, bool callee_interpreted); static inline intptr_t* frame_bottom(const frame& f); - static inline intptr_t* sender_unextended_sp(const frame& f); + static inline intptr_t* callers_sp(const frame& f); static inline int stack_argsize(const frame& f); static inline address* return_pc_address(const frame& f); From 4c27b76629b0b24b9ac711470129d399434cf798 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Sat, 5 Nov 2022 09:33:49 +0100 Subject: [PATCH 03/11] Changes lost in merge --- .../cpu/riscv/continuationFreezeThaw_riscv.inline.hpp | 6 ++++++ src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp index da68f58527458..9f69f681ba365 100644 --- a/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationFreezeThaw_riscv.inline.hpp @@ -204,6 +204,12 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) { *(intptr_t**)(sp - 2) = fp; } +template +inline void Thaw::patch_caller_links(intptr_t* sp, intptr_t* bottom) { + // Fast path depends on !PreserveFramePointer. See can_thaw_fast(). + assert(!PreserveFramePointer, "Frame pointers need to be fixed"); +} + // Slow path inline frame ThawBase::new_entry_frame() { diff --git a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp index 66ee7e375eda6..52d16466035d0 100644 --- a/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/continuationHelper_riscv.inline.hpp @@ -100,7 +100,8 @@ inline address* ContinuationHelper::InterpretedFrame::return_pc_address(const fr return (address*)(f.fp() + frame::return_addr_offset); } -inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, intptr_t* sp) { +inline void ContinuationHelper::InterpretedFrame::patch_sender_sp(frame& f, const frame& caller) { + intptr_t* sp = caller.unextended_sp(); assert(f.is_interpreted_frame(), ""); intptr_t* la = f.addr_at(frame::interpreter_frame_sender_sp_offset); *la = f.is_heap_frame() ? (intptr_t)(sp - f.fp()) : (intptr_t)sp; From c1d2f8787d29cecf8e1fad320c4dc48a7c2a424a Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Sat, 5 Nov 2022 10:25:19 +0100 Subject: [PATCH 04/11] Fix cpp condition and add PPC64 --- src/hotspot/share/oops/stackChunkOop.inline.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index 036a0c019197a..ce751e2d276fd 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -319,7 +319,7 @@ inline void stackChunkOopDesc::copy_from_stack_to_chunk(intptr_t* from, intptr_t assert(to >= start_address(), "Chunk underflow"); assert(to + size <= end_address(), "Chunk overflow"); -#if !defined(AMD64) || !defined(AARCH64) || !defined(RISCV64) || defined(ZERO) +#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) || defined(ZERO) // Suppress compilation warning-as-error on unimplemented architectures // that stub out arch-specific methods. Some compilers are smart enough // to figure out the argument is always null and then warn about it. @@ -338,7 +338,7 @@ inline void stackChunkOopDesc::copy_from_chunk_to_stack(intptr_t* from, intptr_t assert(from >= start_address(), ""); assert(from + size <= end_address(), ""); -#if !defined(AMD64) || !defined(AARCH64) || !defined(RISCV64) || defined(ZERO) +#if !(defined(AMD64) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) || defined(ZERO) // Suppress compilation warning-as-error on unimplemented architectures // that stub out arch-specific methods. Some compilers are smart enough // to figure out the argument is always null and then warn about it. From f42de6b7b4ee719d777dc44d922b2db20ef911be Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Wed, 16 Nov 2022 09:24:12 +0100 Subject: [PATCH 05/11] Feedback from backwaterred --- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index aeb9b9dfff99f..b9fb7dbc7584d 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1690,7 +1690,7 @@ static void fill_continuation_entry(MacroAssembler* masm, Register reg_cont_obj, DEBUG_ONLY(__ block_comment("fill {")); #ifdef ASSERT - __ load_const_optimized(tmp2, 0x1234); + __ load_const_optimized(tmp2, ContinuationEntry::cookie_value()); __ stw(tmp2, in_bytes(ContinuationEntry::cookie_offset()), R1_SP); #endif //ASSERT From 7276a8ec1dfbbd67bc2d0857762475994891e1b2 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Wed, 16 Nov 2022 11:16:50 +0100 Subject: [PATCH 06/11] Cleanup BasicExp.java --- test/jdk/jdk/internal/vm/Continuation/BasicExp.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java index 629007ac83441..f70cdd462e798 100644 --- a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java @@ -198,7 +198,7 @@ public static void main(String[] args) { // // Run tests with C1 compilations // compLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE; - compPolicySelection = Integer.parseInt(args[0]); + compPolicySelection = Integer.parseInt(args[0]); callSystemGC = Integer.parseInt(args[1]) == 1; runTests(); } catch (Throwable t) { @@ -207,7 +207,6 @@ public static void main(String[] args) { } public static void runTests() { - // TODO: enable System.out.println("$$$0 Running test cases with the following settings:"); System.out.println("compLevel=" + compLevel); System.out.println("callSystemGC=" + callSystemGC); @@ -244,6 +243,11 @@ public static void runTests(CompilationPolicy compPolicy) { new ContinuationDeepRecursionStackargs().runTestCase(3, compPolicy); } + // Control which frames are compiled/interpreted when calling Continuation.yield() + // With COMP_WINDOW the methods in the window are supposed to be compiled and others + // are interpreted. With DEOPT_WINDOW vice versa. + // The methods that are subject to the CompilationPolicy are set with setMethods(). + // Their order has to correspond to the stack order when calling yield(). public static class CompilationPolicy { public int warmupIterations; public Pattern methodPattern; @@ -338,7 +342,7 @@ public void setMethods(Method[] methods) { } public boolean shiftWindow() { - if(compWindowMode == CompWindowMode.NO_COMP_WINDOW) return false; + if (compWindowMode == CompWindowMode.NO_COMP_WINDOW) return false; if (++winPos == methods.length) { winPos = 0; if (compWindowMode == CompWindowMode.DEOPT_WINDOW) { @@ -480,7 +484,7 @@ public void testEntry_dontinline() { do { try { cont.run(); - } catch(UnhandledException e) { + } catch (UnhandledException e) { log_dontjit("Exc: " + e); } if (callSystemGC) System.gc(); From 116839ee9b56953b5bc262836d7ac2a657c57dc1 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Fri, 18 Nov 2022 11:10:15 +0100 Subject: [PATCH 07/11] Feedback Martin --- src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp | 6 +++--- src/hotspot/cpu/ppc/frame_ppc.hpp | 2 +- src/hotspot/cpu/ppc/frame_ppc.inline.hpp | 2 +- src/hotspot/cpu/ppc/nativeInst_ppc.cpp | 2 +- src/hotspot/cpu/ppc/ppc.ad | 6 +++--- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 2 +- src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp | 6 +++--- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp index ea0690ea45acc..ece8cbc9af933 100644 --- a/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/continuationFreezeThaw_ppc.inline.hpp @@ -494,8 +494,8 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& intptr_t* frame_sp = caller.unextended_sp() + overlap - fsize; intptr_t* fp = frame_sp + (hf.fp() - heap_sp); // align fp - int padding = fp - align_down(fp, frame::frame_alignment); - fp -= padding; + int padding = fp - align_down(fp, frame::frame_alignment); + fp -= padding; // alignment of sp is done by callee or in finish_thaw() frame_sp -= padding; @@ -517,7 +517,7 @@ template frame ThawBase::new_stack_frame(const frame& hf, frame& intptr_t* frame_sp = caller.sp() - fsize; if ((bottom && argsize > 0) || caller.is_interpreted_frame()) { - frame_sp -= argsize + frame::metadata_words_at_top; + frame_sp -= argsize + frame::metadata_words_at_top; frame_sp = align_down(frame_sp, frame::alignment_in_bytes); caller.set_sp(frame_sp + fsize); } diff --git a/src/hotspot/cpu/ppc/frame_ppc.hpp b/src/hotspot/cpu/ppc/frame_ppc.hpp index e3c94ac06cea0..1a0c66732bd36 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.hpp @@ -367,7 +367,7 @@ }; union { - intptr_t* _fp; // frame pointer + intptr_t* _fp; // frame pointer int _offset_fp; // relative frame pointer for use in stack-chunk frames }; diff --git a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp index 27df0ce7ffe70..ababc1b8c59bd 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp @@ -110,7 +110,7 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address _on_heap(on_heap), DEBUG_ONLY(_frame_index(-1) COMMA) _unextended_sp(unextended_sp), _fp(fp) { // In thaw, non-heap frames use this constructor to pass oop_map. I don't know why. assert(_on_heap || _cb != nullptr, "these frames are always heap frames"); - if (cb != NULL) { + if (cb != nullptr) { setup(); } #ifdef ASSERT diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp index 19ad9dab518c0..6b0e96956e9af 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp @@ -445,7 +445,7 @@ bool NativeDeoptInstruction::is_deopt_at(address code_pos) { return nm->verified_entry_point() != code_pos; } -// Inserts an undefined instruction at a given pc +// Inserts an instruction which is specified to cause a SIGILL at a given pc void NativeDeoptInstruction::insert(address code_pos) { ResourceMark rm; int code_size = 1 * BytesPerInstWord; diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 4a6f36136605f..296bf1600891e 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -14383,7 +14383,7 @@ instruct CallStaticJavaDirect(method meth) %{ ins_num_consts(3 /* up to 3 patchable constants: inline cache, 2 call targets. */); format %{ "CALL,static $meth \t// ==> " %} - size(8); + size(Continuations::enabled() ? 8 : 4); ins_encode( enc_java_static_call(meth) ); ins_pipe(pipe_class_call); %} @@ -14404,7 +14404,7 @@ instruct CallDynamicJavaDirectSched(method meth) %{ ins_num_consts(1 /* 1 patchable constant: call destination */); format %{ "BL \t// dynamic $meth ==> " %} - size(8); + size(Continuations::enabled() ? 8 : 4); ins_encode( enc_java_dynamic_call_sched(meth) ); ins_pipe(pipe_class_call); %} @@ -14482,7 +14482,7 @@ instruct CallLeafDirect(method meth) %{ predicate(false); // but never match. format %{ "BCTRL \t// leaf call $meth ==> " %} - size(8); + size(Continuations::enabled() ? 8 : 4); ins_encode %{ __ bctrl(); __ post_call_nop(); diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index b9fb7dbc7584d..1ae846ae9351b 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -1800,7 +1800,7 @@ static void gen_continuation_enter(MacroAssembler* masm, #endif // Read interpreter arguments into registers (this is an ad-hoc i2c adapter) - __ ld(reg_cont_obj, Interpreter::stackElementSize*3, R15_esp); + __ ld(reg_cont_obj, Interpreter::stackElementSize*3, R15_esp); __ lwz(reg_is_cont, Interpreter::stackElementSize*2, R15_esp); __ lwz(reg_is_virtual, Interpreter::stackElementSize*1, R15_esp); diff --git a/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp b/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp index ffccb00917538..06b0f045f1c8e 100644 --- a/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/stackChunkFrameStream_ppc.inline.hpp @@ -189,9 +189,9 @@ inline int StackChunkFrameStream::interpreter_frame_num_oops() const frame f = to_frame(); f.interpreted_frame_oop_map(&mask); return mask.num_oops() - + 1 // for the mirror oop - + ((intptr_t*)f.interpreter_frame_monitor_begin() - - (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); + + 1 // for the mirror oop + + ((intptr_t*)f.interpreter_frame_monitor_begin() + - (intptr_t*)f.interpreter_frame_monitor_end())/BasicObjectLock::size(); } template<> From 224307503d0abbf34720275e851501b47f175a96 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Mon, 21 Nov 2022 13:09:47 +0100 Subject: [PATCH 08/11] Cleanup BasicExp test --- .../internal/vm/Continuation/BasicExp.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java index f70cdd462e798..afe7833cf19ea 100644 --- a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java @@ -26,15 +26,14 @@ * @test id=all-policies-no-system-gc * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* @@ -47,16 +46,15 @@ * @test id=policy-1-gc-after-yield * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -68,16 +66,15 @@ * @test id=policy-2-gc-after-yield * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -89,16 +86,15 @@ * @test id=policy-3-gc-after-yield * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -110,16 +106,15 @@ * @test id=policy-4-gc-after-yield * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -131,16 +126,15 @@ * @test id=policy-5-gc-after-yield * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4) - * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true + * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* From 5cf90744cad138cd07b8bd4185194d0d9ec53cb3 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Tue, 22 Nov 2022 14:50:33 +0100 Subject: [PATCH 09/11] Feedback Leonid --- .../internal/vm/Continuation/BasicExp.java | 372 +++++++++++++----- 1 file changed, 272 insertions(+), 100 deletions(-) diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java index afe7833cf19ea..a513791c195b0 100644 --- a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java @@ -23,37 +23,123 @@ */ /** - * @test id=all-policies-no-system-gc + * @test id=policy-1-without-gc-with-verification * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 0 0 + * BasicExp 1 0 */ /** - * @test id=policy-1-gc-after-yield + * @test id=policy-2-without-gc-with-verification * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 2 0 + */ + +/** + * @test id=policy-3-without-gc-with-verification + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 3 0 + */ + +/** + * @test id=policy-4-without-gc-with-verification + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 4 0 + */ + +/** + * @test id=policy-5-without-gc-with-verification + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+IgnoreUnrecognizedVMOptions -XX:+VerifyContinuations -Xbatch -XX:-TieredCompilation + * -XX:CompileCommand=dontinline,*::*dontinline* + * -XX:CompileCommand=dontinline,*::*dontjit* + * -XX:CompileCommand=exclude,*::*dontjit* + * -XX:CompileCommand=dontinline,java/lang/String*.* + * BasicExp 5 0 + */ + +/** + * @test id=policy-1-with-gc-without-verification + * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). + * @requires vm.continuations + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview + * @modules java.base/jdk.internal.vm + * @library /test/lib /test/hotspot/jtreg + * @build java.base/java.lang.StackWalkerHelper + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* @@ -63,17 +149,18 @@ */ /** - * @test id=policy-2-gc-after-yield + * @test id=policy-2-with-gc-without-verification * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* @@ -83,17 +170,18 @@ */ /** - * @test id=policy-3-gc-after-yield + * @test id=policy-3-with-gc-without-verification * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* @@ -103,17 +191,18 @@ */ /** - * @test id=policy-4-gc-after-yield + * @test id=policy-4-with-gc-without-verification * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* @@ -123,17 +212,18 @@ */ /** - * @test id=policy-5-gc-after-yield + * @test id=policy-5-with-gc-without-verification * @summary Collection of basic continuation tests. CompilationPolicy controls which frames in a sequence should be compiled when calling Continuation.yield(). * @requires vm.continuations - * @requires vm.flavor == "server" & (vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == false) + * @requires vm.flavor == "server" & vm.opt.TieredCompilation != true + * @enablePreview * @modules java.base/jdk.internal.vm * @library /test/lib /test/hotspot/jtreg * @build java.base/java.lang.StackWalkerHelper * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * - * @run main/othervm/timeout=300 --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* @@ -166,7 +256,7 @@ public class BasicExp { public static final Pattern CONT_METHS = Pattern.compile("^(enter|enter0|yield|yield0)$"); public static int compPolicySelection; - public static boolean callSystemGC; + public static boolean triggerGCAfterYield; public static int compLevel; public static final WhiteBox WB = WhiteBox.getWhiteBox(); @@ -193,7 +283,7 @@ public static void main(String[] args) { // compLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE; compPolicySelection = Integer.parseInt(args[0]); - callSystemGC = Integer.parseInt(args[1]) == 1; + triggerGCAfterYield = Integer.parseInt(args[1]) == 1; runTests(); } catch (Throwable t) { t.printStackTrace(); @@ -203,18 +293,24 @@ public static void main(String[] args) { public static void runTests() { System.out.println("$$$0 Running test cases with the following settings:"); System.out.println("compLevel=" + compLevel); - System.out.println("callSystemGC=" + callSystemGC); + System.out.println("callSystemGC=" + triggerGCAfterYield); System.out.println(); WB.deoptimizeAll(); boolean all = compPolicySelection == 0; - if (compPolicySelection == 1 || all) runTests(new CompilationPolicy(7 /*warmup*/, COMP_NONE, COMP_NONE /*Cont. pattern*/)); - if (compPolicySelection == 2 || all) runTests(new CompilationPolicy(7 /*warmup*/, 1 /* length comp. window */)); - if (compPolicySelection == 3 || all) runTests(new CompilationPolicy(7 /*warmup*/, 2 /* length comp. window */)); - if (compPolicySelection == 4 || all) runTests(new CompilationPolicy(7 /*warmup*/, 3 /* length comp. window */)); - if (compPolicySelection == 5 || all) runTests(new CompilationPolicy(7 /*warmup*/, COMP_ALL, CONT_METHS /*Cont. pattern*/)); - if (compPolicySelection >= 6) throw new Error("CompilationPolicy with number " + compPolicySelection + " does not exist"); + if (compPolicySelection == 1 || all) + runTests(new CompilationPolicy(7 /*warmup*/, COMP_NONE, COMP_NONE /*Cont. pattern*/)); + if (compPolicySelection == 2 || all) + runTests(new CompilationPolicy(7 /*warmup*/, 1 /* length comp. window */)); + if (compPolicySelection == 3 || all) + runTests(new CompilationPolicy(7 /*warmup*/, 2 /* length comp. window */)); + if (compPolicySelection == 4 || all) + runTests(new CompilationPolicy(7 /*warmup*/, 3 /* length comp. window */)); + if (compPolicySelection == 5 || all) + runTests(new CompilationPolicy(7 /*warmup*/, COMP_ALL, CONT_METHS /*Cont. pattern*/)); + if (compPolicySelection >= 6) + throw new Error("CompilationPolicy with number " + compPolicySelection + " does not exist"); } public static void runTests(CompilationPolicy compPolicy) { @@ -257,15 +353,20 @@ public enum CompWindowMode { NO_COMP_WINDOW, COMP_WINDOW, DEOPT_WINDOW } - public CompilationPolicy(int warmupIterations, Pattern methodPattern, Pattern contMethPattern) { - this(warmupIterations, 0, methodPattern, contMethPattern, CompWindowMode.NO_COMP_WINDOW); + public CompilationPolicy(int warmupIterations, Pattern methodPattern, + Pattern contMethPattern) { + this(warmupIterations, 0, methodPattern, contMethPattern, + CompWindowMode.NO_COMP_WINDOW); } - public CompilationPolicy(int warmupIterations, int windowLength, Pattern methodPattern, Pattern contMethPattern) { - this(warmupIterations, windowLength, methodPattern, contMethPattern, CompWindowMode.COMP_WINDOW); + public CompilationPolicy(int warmupIterations, int windowLength, + Pattern methodPattern, Pattern contMethPattern) { + this(warmupIterations, windowLength, methodPattern, contMethPattern, + CompWindowMode.COMP_WINDOW); } - public CompilationPolicy(int warmupIterations, int windowLength, Pattern methodPattern, Pattern contMethPattern, + public CompilationPolicy(int warmupIterations, int windowLength, + Pattern methodPattern, Pattern contMethPattern, CompWindowMode startMode) { this.warmupIterations = warmupIterations; this.methodPattern = methodPattern; @@ -289,21 +390,22 @@ public boolean compileMethods() { print(); for (int i = 0; i < methods.length; i++) { Method meth = methods[i]; - boolean inWindow = i >= winPos && i < (winPos+winLen); + boolean inWindow = i >= winPos && i < (winPos + winLen); boolean shouldBeCompiled = compWindowMode == CompWindowMode.NO_COMP_WINDOW || (inWindow && compWindowMode == CompWindowMode.COMP_WINDOW) || (!inWindow && compWindowMode == CompWindowMode.DEOPT_WINDOW); boolean isCompiled = WB.isMethodCompiled(meth); - log("methods["+i+"] inWindow="+inWindow + " isCompiled="+isCompiled+" shouldBeCompiled="+shouldBeCompiled+" method=`"+meth+"`"); + log("methods[" + i + "] inWindow=" + inWindow + " isCompiled=" + isCompiled + + " shouldBeCompiled=" + shouldBeCompiled + " method=`" + meth + "`"); if (isCompiled != shouldBeCompiled) { if (shouldBeCompiled) { - log(" Compiling methods["+i+"]"); + log(" Compiling methods[" + i + "]"); enqForCompilation(meth); newCompilation = true; assertTrue(WB.isMethodCompiled(meth), "Run with -Xbatch"); } else { assertFalse(WB.isMethodQueuedForCompilation(meth), "Run with -Xbatch"); - log(" Deoptimizing methods["+i+"]"); + log(" Deoptimizing methods[" + i + "]"); WB.deoptimizeMethod(meth); } } @@ -379,7 +481,8 @@ public void log_dontjit(String m) { public void runTestCase(int yieldCalls, CompilationPolicy compPolicy) { this.yieldCalls = yieldCalls; - log_dontjit(">>>> Executing test case " + getClass().getName() + " (yieldCalls=" + yieldCalls + ", " + "testVariant=" + testVariant + ")"); + log_dontjit(">>>> Executing test case " + getClass().getName() + + " (yieldCalls=" + yieldCalls + ", " + "testVariant=" + testVariant + ")"); init(compPolicy); try { log_dontjit("Warm-up test case"); @@ -448,11 +551,11 @@ public int compare(Method m1, Method m2) { int p2 = -1; int i = n1.indexOf("ord"); if (i >= 0) { - p1 = Integer.parseInt(n1.substring(i+3, i+6)); + p1 = Integer.parseInt(n1.substring(i + 3, i + 6)); } i = n2.indexOf("ord"); if (i >= 0) { - p2 = Integer.parseInt(n2.substring(i+3, i+6)); + p2 = Integer.parseInt(n2.substring(i + 3, i + 6)); } if (p1 < 0) p1 = getScoreKnownMethods(n1); if (p2 < 0) p2 = getScoreKnownMethods(n2); @@ -481,7 +584,7 @@ public void testEntry_dontinline() { } catch (UnhandledException e) { log_dontjit("Exc: " + e); } - if (callSystemGC) System.gc(); + if (triggerGCAfterYield) WB.youngGC(); checkFrames_dontjit(cont); } while (!cont.isDone()); } @@ -502,9 +605,11 @@ public void sleep(Duration d) { } static final long i1=1; static final long i2=2; static final long i3=3; - static final long i4=4; static final long i5=5; static final long i6=6; static final long i7=7; - static final long i8=8; static final long i9=9; static final long i10=10; static final long i11=11; static final long i12=12; - static final long i13=13; static final long i14=14; static final long i15=15; static final long i16=16; + static final long i4=4; static final long i5=5; static final long i6=6; + static final long i7=7; static final long i8=8; static final long i9=9; + static final long i10=10; static final long i11=11; static final long + i12=12; static final long i13=13; static final long i14=14; static final + long i15=15; static final long i16=16; } /** @@ -520,7 +625,7 @@ public void run() { log_dontjit("Yield #" + i); String s1 = "str1"; Continuation.yield(THE_SCOPE); - String s2 = s1+"str2"; + String s2 = s1 + "str2"; sField = s2; } } @@ -587,21 +692,25 @@ public void run() { String s1 = "str1"; String result = ord101_testMethod_dontinline(i1, i2, i3, s1); assertEQ(resLong, testVariant == TestCaseVariants.ALLOC_MONITOR ? 7L : 6L); - assertEQ(result, testVariant == TestCaseVariants.EXPR_STACK_NOT_EMPTY ? "exprStckVal str1str2str3" : "str1str2str3"); + assertEQ(result, testVariant == TestCaseVariants.EXPR_STACK_NOT_EMPTY ? + "exprStckVal str1str2str3" : "str1str2str3"); } catch (HandledException e) { caughtException = e; } - assertTrue(testVariant != TestCaseVariants.THROW_HANDLED_EXCEPTION || (caughtException instanceof HandledException), + assertTrue(testVariant != TestCaseVariants.THROW_HANDLED_EXCEPTION + || (caughtException instanceof HandledException), "Exception handling error"); } } - public String ord101_testMethod_dontinline(long a1, long a2, long a3, String s1) throws HandledException { - String s2 = s1+"str2"; + public String ord101_testMethod_dontinline(long a1, long a2, long a3, String s1) + throws HandledException { + String s2 = s1 + "str2"; return ord102_testMethod_dontinline(a1, a2, a3, s2); } - public String ord102_testMethod_dontinline(long a1, long a2, long a3, String s2) throws HandledException { + public String ord102_testMethod_dontinline(long a1, long a2, long a3, String s2) + throws HandledException { if (testVariant == TestCaseVariants.ALLOC_MONITOR) { synchronized (this) { resLong++; @@ -615,14 +724,18 @@ public String ord102_testMethod_dontinline(long a1, long a2, long a3, String s2) } } - public String ord103_testMethod_dontinline(long a1, long a2, long a3, String s2) throws HandledException { + public String ord103_testMethod_dontinline(long a1, long a2, long a3, String s2) + throws HandledException { return ord104_testMethod_dontinline(a1, a2, a3, s2); } - public String ord104_testMethod_dontinline(long a1, long a2, long a3, String s2) throws HandledException { + public String ord104_testMethod_dontinline(long a1, long a2, long a3, String s2) + throws HandledException { long res = a2; - String s3 = s2+"str3"; - log_dontjit("Yield #" + yieldCount++); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount); + String s3 = s2 + "str3"; + log_dontjit("Yield #" + yieldCount++); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount); if (testVariant == TestCaseVariants.THROW_HANDLED_EXCEPTION) { log_dontjit("Exc: throw handled"); throw new HandledException(); @@ -631,7 +744,7 @@ public String ord104_testMethod_dontinline(long a1, long a2, long a3, String s2) log_dontjit("Exc: throw unhandled"); throw new UnhandledException(); } - resLong += res+a1+a3; + resLong += res + a1 + a3; return s3; } @@ -641,10 +754,15 @@ public String putOnExprStack_testMethod_dontjit_dontinline() { @Override public void checkFrames_dontjit(Continuation cont) { - List frames = cont.stackWalker().walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); + List frames = + cont.stackWalker() + .walk(fs -> fs.map(StackWalker.StackFrame::getMethodName).collect(Collectors.toList())); assertEquals(frames, cont.isDone() ? List.of() - : Arrays.asList("yield", "ord104_testMethod_dontinline", "ord103_testMethod_dontinline", - "ord102_testMethod_dontinline", "ord101_testMethod_dontinline", "run", "enter0", "enter")); + : Arrays.asList("yield", "ord104_testMethod_dontinline", + "ord103_testMethod_dontinline", + "ord102_testMethod_dontinline", + "ord101_testMethod_dontinline", + "run", "enter0", "enter")); } } @@ -726,26 +844,30 @@ public void setup_dontjit(boolean warmup) { public void run() { log_dontjit("Continuation running on thread " + Thread.currentThread()); long res = ord101_recurse_dontinline(0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11); - if (res != i1+i2+i3+i4+i5+i6+i7+i8+i9+i10+i11) { + if (res != i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8 + i9 + i10 + i11) { throw new Error(); } } - public long ord101_recurse_dontinline(int depth, long l1, long l2, long l3, long l4, long l5, long l6, long l7, long l8, long l9, long l10, long l11) { + public long ord101_recurse_dontinline(int depth, long l1, long l2, long + l3, long l4, long l5, long l6, + long l7, long l8, long l9, long + l10, long l11) { if (depth >= limit) { log_dontjit("yield at depth " + depth); ord102_yield_dontinline(0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); log_dontjit("After yield at depth " + depth); - return l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11; + return l1 + l2 + l3 + l4 + l5 + l6 + l7 + l8 + l9 + l10 + l11; } - long res = ord101_recurse_dontinline(depth + 1, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); - if (res != l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11) { + long res = ord101_recurse_dontinline(depth + 1, l1, l2, l3, l4, l5, + l6, l7, l8, l9, l10, l11); + if (res != l1 + l2 + l3 + l4 + l5 + l6 + l7 + l8 + l9 + l10 + l11) { throw new Error(); } if (depth == yield1_depth || depth == yield2_depth) { log_dontjit("yield at depth " + depth); long res1 = ord102_yield_dontinline(0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); - if (res1 != l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11) { + if (res1 != l1 + l2 + l3 + l4 + l5 + l6 + l7 + l8 + l9 + l10 + l11) { throw new Error(); } log_dontjit("After yield at depth " + depth); @@ -754,13 +876,14 @@ public long ord101_recurse_dontinline(int depth, long l1, long l2, long l3, long } // Add a few frames before yield - public long ord102_yield_dontinline(int depth, long l1, long l2, long l3, long l4, long l5, long l6, long l7, long l8, long l9, long l10, long l11) { + public long ord102_yield_dontinline(int depth, long l1, long l2, long l3, long l4, long l5, + long l6, long l7, long l8, long l9, long l10, long l11) { if (depth >= 2) { Continuation.yield(THE_SCOPE); - return l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11; + return l1 + l2 + l3 + l4 + l5 + l6 + l7 + l8 + l9 + l10 + l11; } long res = ord102_yield_dontinline(depth + 1, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11); - if (res != l1+l2+l3+l4+l5+l6+l7+l8+l9+l10+l11) { + if (res != l1 + l2 + l3 + l4 + l5 + l6 + l7 + l8 + l9 + l10 + l11) { throw new Error(); } return res; @@ -792,9 +915,12 @@ public long ord101_testMethod_dontinline() { return res; } - public long ord102_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, - long a9, long a10, long a11) { - long res = a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+i12+i13+i14+i15+i16; + public long ord102_testMethod_dontinline(long a1, long a2, long a3, long + a4, long a5, long a6, long a7, + long a8, long a9, long a10, + long a11) { + long res = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + + i12 + i13 + i14 + i15 + i16; log_dontjit("Yield #" + yieldCount); log_dontjit("ord109_testMethod_dontinline res=" + res); Continuation.yield(THE_SCOPE); @@ -829,9 +955,13 @@ public long ord101_testMethod_dontinline() { return res; } - public long ord102_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, - long a9, long a10, long a11, long a12, long a13, long a14) { - long res = ord103_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, i15); + public long ord102_testMethod_dontinline(long a1, long a2, long a3, long + a4, long a5, long a6, long a7, + long a8, long a9, long a10, + long a11, long a12, long a13, + long a14) { + long res = ord103_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, i15); log_dontjit("Yield #" + yieldCount); log_dontjit("ord109_testMethod_dontinline res=" + res); Continuation.yield(THE_SCOPE); @@ -840,9 +970,13 @@ public long ord102_testMethod_dontinline(long a1, long a2, long a3, long a4, lon return res; } - public long ord103_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, - long a8, long a9, long a10, long a11, long a12, long a13, long a14, long a15) { - long res = a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15+i16; + public long ord103_testMethod_dontinline(long a1, long a2, long a3, long + a4, long a5, long a6, long a7, + long a8, long a9, long a10, + long a11, long a12, long a13, + long a14, long a15) { + long res = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + + a12 + a13 + a14 + a15 + i16; log_dontjit("Yield #" + yieldCount); log_dontjit("ord109_testMethod_dontinline res=" + res); Continuation.yield(THE_SCOPE); @@ -868,41 +1002,54 @@ public void run() { public long ord101_testMethod_dontinline(long a1) { long res = ord102_testMethod_dontinline(a1, i2); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } public long ord102_testMethod_dontinline(long a1, long a2) { long res = ord103_testMethod_dontinline(a1, a2, i3); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } public long ord103_testMethod_dontinline(long a1, long a2, long a3) { long res = ord104_testMethod_dontinline(a1, a2, a3, i4); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } public long ord104_testMethod_dontinline(long a1, long a2, long a3, long a4) { long res = ord105_testMethod_dontinline(a1, a2, a3, a4, i5); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } public long ord105_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5) { long res = ord106_testMethod_dontinline(a1, a2, a3, a4, a5, i6); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } public long ord106_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6) { long res = ord107_testMethod_dontinline(a1, a2, a3, a4, a5, a6, i7); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord107_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7) { + public long ord107_testMethod_dontinline(long a1, long a2, long a3, long + a4, long a5, long a6, long a7) { long res = ord108_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, i8); log_dontjit("Yield #" + yieldCount); log_dontjit("ord108_testMethod_dontinline res=" + res); @@ -912,7 +1059,8 @@ public long ord107_testMethod_dontinline(long a1, long a2, long a3, long a4, lon return res; } - public long ord108_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { + public long ord108_testMethod_dontinline(long a1, long a2, long a3, long + a4, long a5, long a6, long a7, long a8) { long res = ord109_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, i9); log_dontjit("Yield #" + yieldCount); log_dontjit("ord109_testMethod_dontinline res=" + res); @@ -922,52 +1070,76 @@ public long ord108_testMethod_dontinline(long a1, long a2, long a3, long a4, lon return res; } - public long ord109_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9) { + public long ord109_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9) { long res = ord110_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, i10); Continuation.yield(THE_SCOPE); return res; } - public long ord110_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10) { + public long ord110_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10) { long res = ord111_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, i11); log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord111_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11) { + public long ord111_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10, long a11) { long res = ord112_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, i12); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord112_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12) { + public long ord112_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10, long a11, long a12) { long res = ord113_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, i13); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord113_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13) { + public long ord113_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10, long a11, long a12, + long a13) { long res = ord114_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, i14); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord114_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13, long a14) { + public long ord114_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10, long a11, long a12, + long a13, long a14) { long res = ord115_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, i15); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord115_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13, long a14, long a15) { + public long ord115_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10, long a11, long a12, + long a13, long a14, long a15) { long res = ord116_testMethod_dontinline(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, i16); - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); return res; } - public long ord116_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8, long a9, long a10, long a11, long a12, long a13, long a14, long a15, long a16) { - long res = a2+a4+a6+a8+a10+a12+a14+a16; - log_dontjit("Yield #" + yieldCount); Continuation.yield(THE_SCOPE); log_dontjit("/Yield #" + yieldCount++); - res += a1+a3+a5+a7+a9+a11+a13+a15; + public long ord116_testMethod_dontinline(long a1, long a2, long a3, long a4, long a5, long a6, + long a7, long a8, long a9, long a10, long a11, long a12, + long a13, long a14, long a15, long a16) { + long res = a2 + a4 + a6 + a8 + a10 + a12 + a14 + a16; + log_dontjit("Yield #" + yieldCount); + Continuation.yield(THE_SCOPE); + log_dontjit("/Yield #" + yieldCount++); + res += a1 + a3 + a5 + a7 + a9 + a11 + a13 + a15; return res; } } From 5f59f2cfbc5da3374bd81ed51f2bbedae1160e77 Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Tue, 22 Nov 2022 21:23:00 +0100 Subject: [PATCH 10/11] More Feedback Leonid --- test/jdk/jdk/internal/vm/Continuation/BasicExp.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java index a513791c195b0..87c9f641d8282 100644 --- a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExp.java @@ -140,7 +140,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation + * -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -161,7 +161,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation + * -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -182,7 +182,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation + * -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -203,7 +203,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation + * -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* @@ -224,7 +224,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. - * -XX:+IgnoreUnrecognizedVMOptions -Xbatch -XX:-TieredCompilation + * -Xbatch -XX:-TieredCompilation * -XX:CompileCommand=dontinline,*::*dontinline* * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* From 817ec63bd72578edda8886505f5af114e776716e Mon Sep 17 00:00:00 2001 From: Richard Reingruber Date: Fri, 25 Nov 2022 12:04:05 +0100 Subject: [PATCH 11/11] Rename test BasicExp -> BasicExt --- .../{BasicExp.java => BasicExt.java} | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) rename test/jdk/jdk/internal/vm/Continuation/{BasicExp.java => BasicExt.java} (99%) diff --git a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java b/test/jdk/jdk/internal/vm/Continuation/BasicExt.java similarity index 99% rename from test/jdk/jdk/internal/vm/Continuation/BasicExp.java rename to test/jdk/jdk/internal/vm/Continuation/BasicExt.java index 87c9f641d8282..f1274696d0fae 100644 --- a/test/jdk/jdk/internal/vm/Continuation/BasicExp.java +++ b/test/jdk/jdk/internal/vm/Continuation/BasicExt.java @@ -40,7 +40,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 1 0 + * BasicExt 1 0 */ /** @@ -61,7 +61,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 2 0 + * BasicExt 2 0 */ /** @@ -82,7 +82,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 3 0 + * BasicExt 3 0 */ /** @@ -103,7 +103,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 4 0 + * BasicExt 4 0 */ /** @@ -124,7 +124,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 5 0 + * BasicExt 5 0 */ /** @@ -145,7 +145,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 1 1 + * BasicExt 1 1 */ /** @@ -166,7 +166,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 2 1 + * BasicExt 2 1 */ /** @@ -187,7 +187,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 3 1 + * BasicExt 3 1 */ /** @@ -208,7 +208,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 4 1 + * BasicExt 4 1 */ /** @@ -229,7 +229,7 @@ * -XX:CompileCommand=dontinline,*::*dontjit* * -XX:CompileCommand=exclude,*::*dontjit* * -XX:CompileCommand=dontinline,java/lang/String*.* - * BasicExp 5 1 + * BasicExt 5 1 */ import java.lang.reflect.Method; @@ -248,7 +248,7 @@ import compiler.whitebox.CompilerWhiteBoxTest; import jdk.test.whitebox.WhiteBox; -public class BasicExp { +public class BasicExt { static final ContinuationScope THE_SCOPE = new ContinuationScope() {}; public static final Pattern COMP_NONE = Pattern.compile("COMP_NONE");