8286302: Port JEP 425 to PPC64
Reviewed-by: tsteele, mdoerr
This commit is contained in:
parent
d6102110e1
commit
43d1173605
32
src/hotspot/cpu/aarch64/continuationEntry_aarch64.hpp
Normal file
32
src/hotspot/cpu/aarch64/continuationEntry_aarch64.hpp
Normal file
@ -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
|
@ -201,6 +201,12 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) {
|
||||
*(intptr_t**)(sp - frame::sender_sp_offset) = fp;
|
||||
}
|
||||
|
||||
template <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::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() {
|
||||
|
@ -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;
|
||||
@ -137,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
|
||||
|
@ -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
|
||||
|
32
src/hotspot/cpu/arm/continuationEntry_arm.hpp
Normal file
32
src/hotspot/cpu/arm/continuationEntry_arm.hpp
Normal file
@ -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
|
@ -91,6 +91,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
template <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::patch_caller_links(intptr_t* sp, intptr_t* bottom) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
inline void ThawBase::prefetch_chunk_pd(void* start, int size) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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); }
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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*/);
|
||||
}
|
||||
|
||||
|
||||
|
35
src/hotspot/cpu/ppc/continuationEntry_ppc.hpp
Normal file
35
src/hotspot/cpu/ppc/continuationEntry_ppc.hpp
Normal file
@ -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
|
@ -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
|
||||
|
@ -29,71 +29,599 @@
|
||||
#include "runtime/frame.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
|
||||
|
||||
inline void FreezeBase::set_top_frame_metadata_pd(const frame& hf) {
|
||||
Unimplemented();
|
||||
inline void patch_callee_link(const frame& f, intptr_t* fp) {
|
||||
*ContinuationHelper::Frame::callee_link_address(f) = fp;
|
||||
}
|
||||
|
||||
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<ConfigT>::patch_caller_links())
|
||||
}
|
||||
|
||||
// Slow path
|
||||
|
||||
template<typename FKind>
|
||||
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());
|
||||
}
|
||||
|
||||
template<typename FKind> frame FreezeBase::new_heap_frame(frame& f, frame& caller) {
|
||||
Unimplemented();
|
||||
return frame();
|
||||
intptr_t* sender_sp = f.sender_sp();
|
||||
address sender_pc = f.sender_pc();
|
||||
assert(sender_sp != f.sp(), "must have changed");
|
||||
|
||||
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<ConfigT>::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<frame_kind>::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<typename FKind>
|
||||
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<frame_kind>::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<frame_kind>::fp()
|
||||
// - UseContinuationFastPath: compiled frames are copied in a batch w/o patching the back link.
|
||||
// The backlinks are restored when thawing (see Thaw<ConfigT>::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 <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::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<typename FKind> 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;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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::patch_pd(frame& f, const frame& caller) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
void ThawBase::patch_chunk_pd(intptr_t* sp) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
inline void ThawBase::prefetch_chunk_pd(void* start, int size) {
|
||||
Unimplemented();
|
||||
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
|
||||
|
@ -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<typename FKind>
|
||||
@ -53,72 +51,135 @@ 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<typename FKind>
|
||||
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);
|
||||
}
|
||||
|
||||
inline intptr_t* ContinuationHelper::InterpretedFrame::callers_sp(const frame& f) {
|
||||
return f.fp();
|
||||
}
|
||||
|
||||
#endif // CPU_PPC_CONTINUATIONFRAMEHELPERS_PPC_INLINE_HPP
|
||||
|
@ -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 {
|
||||
|
@ -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; }
|
||||
|
@ -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 != nullptr) {
|
||||
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,12 +265,86 @@ inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const {
|
||||
return (JavaCallWrapper**)&get_entry_frame_locals()->call_wrapper_address;
|
||||
}
|
||||
|
||||
inline bool frame::is_interpreted_frame() const {
|
||||
return Interpreter::contains(pc());
|
||||
}
|
||||
|
||||
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 (map->in_cont()) { // already in an h-stack
|
||||
return map->stack_chunk()->sender(*this, map);
|
||||
}
|
||||
|
||||
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(RegisterMap* map) const {
|
||||
frame result = sender_raw(map);
|
||||
|
||||
if (map->process_frames() && !map->in_cont()) {
|
||||
StackWatermarkSet::on_iteration(map->thread(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (map->update_map()) {
|
||||
// Tell GC to use argument oopmaps for some runtime stubs that need it.
|
||||
// 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 {
|
||||
return *((oop*)map->location(R3->as_VMReg(), nullptr));
|
||||
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*)map->location(R3->as_VMReg(), nullptr)) = 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 {
|
||||
@ -249,82 +362,24 @@ inline const ImmutableOopMap* frame::get_oop_map() const {
|
||||
}
|
||||
|
||||
inline int frame::compiled_frame_stack_argsize() const {
|
||||
Unimplemented();
|
||||
return 0;
|
||||
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 {
|
||||
Unimplemented();
|
||||
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() {
|
||||
Unimplemented();
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void frame::set_unextended_sp(intptr_t* value) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
inline int frame::offset_unextended_sp() const {
|
||||
Unimplemented();
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void frame::set_offset_unextended_sp(int value) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// frame::sender
|
||||
|
||||
frame frame::sender(RegisterMap* map) const {
|
||||
frame result = sender_raw(map);
|
||||
|
||||
if (map->process_frames()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return frame(sender_sp, sender_pc);
|
||||
return -(int)(_abi0(lr) >> LogBytesPerWord); // offset in words
|
||||
}
|
||||
|
||||
template <typename RegisterMapT>
|
||||
void frame::update_map_with_saved_link(RegisterMapT* map, intptr_t** link_addr) {
|
||||
Unimplemented();
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
#endif // CPU_PPC_FRAME_PPC_INLINE_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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 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;
|
||||
CodeBuffer cb(code_pos, code_size + 1);
|
||||
MacroAssembler* a = new MacroAssembler(&cb);
|
||||
a->illtrap();
|
||||
ICache::ppc64_flush_icache_bytes(code_pos, code_size);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(Continuations::enabled() ? 8 : 4);
|
||||
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(Continuations::enabled() ? 8 : 4);
|
||||
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(Continuations::enabled() ? 8 : 4);
|
||||
ins_encode %{
|
||||
__ bctrl();
|
||||
__ post_call_nop();
|
||||
%}
|
||||
ins_pipe(pipe_class_call);
|
||||
%}
|
||||
|
@ -521,10 +521,10 @@ constexpr 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.
|
||||
constexpr Register R14_bcp = R14;
|
||||
constexpr Register R15_esp = R15;
|
||||
constexpr Register R15_esp = R15; // slot below top of expression stack for ld/st with update
|
||||
constexpr FloatRegister F15_ftos = F15;
|
||||
constexpr Register R16_thread = R16; // address of current thread
|
||||
constexpr Register R17_tos = R17; // address of Java tos (prepushed).
|
||||
constexpr Register R17_tos = R17; // The interpreter's top of (expression) stack cache register
|
||||
constexpr Register R18_locals = R18; // address of first param slot (receiver).
|
||||
constexpr Register R19_method = R19; // address of current method
|
||||
|
||||
|
@ -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, ContinuationEntry::cookie_value());
|
||||
__ 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() {
|
||||
|
@ -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 @@ public:
|
||||
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
|
||||
|
@ -32,39 +32,52 @@
|
||||
#ifdef ASSERT
|
||||
template <ChunkFrames frame_kind>
|
||||
inline bool StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
inline frame StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
inline address StackChunkFrameStream<frame_kind>::get_pc() const {
|
||||
Unimplemented();
|
||||
return NULL;
|
||||
assert(!is_done(), "");
|
||||
return (address)((frame::abi_minframe*) _sp)->lr;
|
||||
}
|
||||
|
||||
template <ChunkFrames frame_kind>
|
||||
inline intptr_t* StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
inline intptr_t* StackChunkFrameStream<frame_kind>::derelativize(int offset) const {
|
||||
Unimplemented();
|
||||
return NULL;
|
||||
intptr_t* fp = this->fp();
|
||||
assert(fp != nullptr, "");
|
||||
return fp + fp[offset];
|
||||
}
|
||||
|
||||
template <ChunkFrames frame_kind>
|
||||
inline intptr_t* StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
@ -75,37 +88,122 @@ intptr_t* StackChunkFrameStream<frame_kind>::next_sp_for_interpreter_frame() con
|
||||
|
||||
template <ChunkFrames frame_kind>
|
||||
inline void StackChunkFrameStream<frame_kind>::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_kind>::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 <ChunkFrames frame_kind>
|
||||
inline int StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
inline int StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
inline int StackChunkFrameStream<frame_kind>::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<ChunkFrames::Mixed>::update_reg_map_pd(RegisterMap* map) {
|
||||
Unimplemented();
|
||||
// Nothing to do (no non-volatile registers in java calling convention)
|
||||
}
|
||||
|
||||
template<>
|
||||
template<>
|
||||
inline void StackChunkFrameStream<ChunkFrames::CompiledOnly>::update_reg_map_pd(RegisterMap* map) {
|
||||
Unimplemented();
|
||||
// Nothing to do (no non-volatile registers in java calling convention)
|
||||
}
|
||||
|
||||
template <ChunkFrames frame_kind>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
32
src/hotspot/cpu/riscv/continuationEntry_riscv.hpp
Normal file
32
src/hotspot/cpu/riscv/continuationEntry_riscv.hpp
Normal file
@ -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
|
@ -204,6 +204,12 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) {
|
||||
*(intptr_t**)(sp - 2) = fp;
|
||||
}
|
||||
|
||||
template <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::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() {
|
||||
|
@ -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;
|
||||
@ -136,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();
|
||||
}
|
||||
|
||||
#endif // CPU_RISCV_CONTINUATIONFRAMEHELPERS_RISCV_INLINE_HPP
|
||||
|
@ -139,6 +139,13 @@
|
||||
|
||||
// size, in words, of frame metadata (e.g. pc and link)
|
||||
metadata_words = 2,
|
||||
// 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
|
||||
|
32
src/hotspot/cpu/s390/continuationEntry_s390.hpp
Normal file
32
src/hotspot/cpu/s390/continuationEntry_s390.hpp
Normal file
@ -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
|
@ -91,6 +91,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
template <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::patch_caller_links(intptr_t* sp, intptr_t* bottom) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
inline void ThawBase::prefetch_chunk_pd(void* start, int size) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
@ -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; }
|
||||
|
32
src/hotspot/cpu/x86/continuationEntry_x86.hpp
Normal file
32
src/hotspot/cpu/x86/continuationEntry_x86.hpp
Normal file
@ -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
|
@ -193,6 +193,12 @@ inline void ThawBase::prefetch_chunk_pd(void* start, int size) {
|
||||
Prefetch::read(start, size - 64);
|
||||
}
|
||||
|
||||
template <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::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;
|
||||
|
@ -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;
|
||||
@ -136,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
|
||||
|
@ -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
|
||||
|
32
src/hotspot/cpu/zero/continuationEntry_zero.hpp
Normal file
32
src/hotspot/cpu/zero/continuationEntry_zero.hpp
Normal file
@ -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
|
@ -91,6 +91,11 @@ void ThawBase::patch_chunk_pd(intptr_t* sp) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
template <typename ConfigT>
|
||||
inline void Thaw<ConfigT>::patch_caller_links(intptr_t* sp, intptr_t* bottom) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
||||
inline void ThawBase::prefetch_chunk_pd(void* start, int size) {
|
||||
Unimplemented();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
************************************************/
|
||||
|
||||
|
@ -105,7 +105,7 @@ void InstanceStackChunkKlass::oop_oop_iterate_header_bounded(stackChunkOop chunk
|
||||
template <typename T, class OopClosureType>
|
||||
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 <typename T, class OopClosureType>
|
||||
void InstanceStackChunkKlass::oop_oop_iterate_stack(stackChunkOop chunk, OopClosureType* closure) {
|
||||
if (chunk->has_bitmap()) {
|
||||
oop_oop_iterate_stack_with_bitmap<T>(chunk, closure, chunk->sp_address() - frame::metadata_words, chunk->end_address());
|
||||
oop_oop_iterate_stack_with_bitmap<T>(chunk, closure, chunk->sp_address() - frame::metadata_words_at_bottom, chunk->end_address());
|
||||
} else {
|
||||
oop_oop_iterate_stack_slow(chunk, closure, chunk->range());
|
||||
}
|
||||
|
@ -498,7 +498,7 @@ public:
|
||||
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<narrowOop> 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<oop> 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;
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ inline void stackChunkOopDesc::set_cont_raw(oop value) { jdk_internal_vm_Sta
|
||||
template<DecoratorSet decorators>
|
||||
inline void stackChunkOopDesc::set_cont_access(oop value) { jdk_internal_vm_StackChunk::set_cont_access<decorators>(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<intptr_t>(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,12 +135,7 @@ inline bool stackChunkOopDesc::is_in_chunk(void* p) const {
|
||||
}
|
||||
|
||||
bool stackChunkOopDesc::is_usable_in_chunk(void* p) const {
|
||||
#if (defined(X86) || defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
|
||||
HeapWord* start = (HeapWord*)start_address() + sp() - frame::metadata_words;
|
||||
#else
|
||||
Unimplemented();
|
||||
HeapWord* start = NULL;
|
||||
#endif
|
||||
HeapWord* start = (HeapWord*)start_address() + sp() - frame::metadata_words_at_bottom;
|
||||
HeapWord* end = start + stack_size();
|
||||
return (HeapWord*)p >= start && (HeapWord*)p < end;
|
||||
}
|
||||
@ -324,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.
|
||||
@ -343,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.
|
||||
|
@ -239,7 +239,7 @@ frame Continuation::continuation_parent_frame(RegisterMap* map) {
|
||||
|
||||
map->set_stack_chunk(nullptr);
|
||||
|
||||
#if (defined(X86) || defined(AARCH64) || defined(RISCV64)) && !defined(ZERO)
|
||||
#if (defined(X86) || defined(AARCH64) || defined(RISCV64) || defined(PPC64)) && !defined(ZERO)
|
||||
frame sender(cont.entrySP(), cont.entryFP(), cont.entryPC());
|
||||
#else
|
||||
frame sender = frame();
|
||||
|
@ -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 @@ private:
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -437,7 +437,7 @@ protected:
|
||||
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_kind>::frame_size()
|
||||
template<typename FKind>
|
||||
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_kind>::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<ChunkFrames::Mixed>(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<ChunkFrames::Mixed>(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,15 +1046,18 @@ 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_kind>::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);
|
||||
|
||||
// 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() NOT_RISCV64(+ 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
|
||||
@ -1055,7 +1065,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 +1084,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<ContinuationHelper::InterpretedFrame>(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 +1109,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_kind>::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 +1678,7 @@ public:
|
||||
|
||||
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 <typename ConfigT>
|
||||
@ -1684,24 +1701,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 +1755,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 +1816,13 @@ NOINLINE intptr_t* Thaw<ConfigT>::thaw_fast(stackChunkOop chunk) {
|
||||
const bool is_last = empty && chunk->is_parent_null<typename ConfigT::OopT>();
|
||||
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 +1832,9 @@ NOINLINE intptr_t* Thaw<ConfigT>::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 +2021,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 +2052,9 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c
|
||||
|
||||
frame f = new_stack_frame<ContinuationHelper::InterpretedFrame>(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 +2063,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 +2123,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 +2160,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 +2233,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 +2263,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);
|
||||
|
@ -90,12 +90,12 @@ public:
|
||||
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);
|
||||
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);
|
||||
|
@ -125,6 +125,11 @@ public:
|
||||
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; }
|
||||
|
@ -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);
|
||||
|
@ -3126,11 +3126,15 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) {
|
||||
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);
|
||||
|
@ -123,10 +123,60 @@ inline bool StackChunkFrameStream<ChunkFrames::CompiledOnly>::is_interpreted() c
|
||||
return false;
|
||||
}
|
||||
|
||||
// StackChunkFrameStream<frame_kind>::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<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
inline int StackChunkFrameStream<frame_kind>::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 <ChunkFrames frame_kind>
|
||||
|
1146
test/jdk/jdk/internal/vm/Continuation/BasicExt.java
Normal file
1146
test/jdk/jdk/internal/vm/Continuation/BasicExt.java
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user