| /* | |
| * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * 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 SHARE_OOPS_STACKCHUNKOOP_INLINE_HPP | |
| #define SHARE_OOPS_STACKCHUNKOOP_INLINE_HPP | |
| #include "oops/stackChunkOop.hpp" | |
| #include "gc/shared/collectedHeap.hpp" | |
| #include "gc/shared/barrierSet.hpp" | |
| #include "gc/shared/barrierSetStackChunk.hpp" | |
| #include "gc/shared/gc_globals.hpp" | |
| #include "memory/memRegion.hpp" | |
| #include "memory/universe.hpp" | |
| #include "oops/access.inline.hpp" | |
| #include "oops/instanceStackChunkKlass.inline.hpp" | |
| #include "runtime/continuationJavaClasses.inline.hpp" | |
| #include "runtime/frame.hpp" | |
| #include "runtime/globals.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "runtime/objectMonitor.hpp" | |
| #include "runtime/registerMap.hpp" | |
| #include "runtime/smallRegisterMap.inline.hpp" | |
| #include "utilities/macros.hpp" | |
| #include CPU_HEADER_INLINE(stackChunkOop) | |
| DEF_HANDLE_CONSTR(stackChunk, is_stackChunk_noinline) | |
| inline stackChunkOop stackChunkOopDesc::cast(oop obj) { | |
| assert(obj == nullptr || obj->is_stackChunk(), "Wrong type"); | |
| return stackChunkOop(obj); | |
| } | |
| inline stackChunkOop stackChunkOopDesc::parent() const { return stackChunkOopDesc::cast(jdk_internal_vm_StackChunk::parent(as_oop())); } | |
| inline void stackChunkOopDesc::set_parent(stackChunkOop value) { jdk_internal_vm_StackChunk::set_parent(this, value); } | |
| template<typename P> | |
| inline void stackChunkOopDesc::set_parent_raw(oop value) { jdk_internal_vm_StackChunk::set_parent_raw<P>(this, value); } | |
| template<DecoratorSet decorators> | |
| inline void stackChunkOopDesc::set_parent_access(oop value) { jdk_internal_vm_StackChunk::set_parent_access<decorators>(this, value); } | |
| inline int stackChunkOopDesc::stack_size() const { return jdk_internal_vm_StackChunk::size(as_oop()); } | |
| inline int stackChunkOopDesc::bottom() const { return jdk_internal_vm_StackChunk::bottom(as_oop()); } | |
| inline void stackChunkOopDesc::set_bottom(int value) { jdk_internal_vm_StackChunk::set_bottom(this, value); } | |
| inline int stackChunkOopDesc::sp() const { return jdk_internal_vm_StackChunk::sp(as_oop()); } | |
| inline void stackChunkOopDesc::set_sp(int value) { jdk_internal_vm_StackChunk::set_sp(this, value); } | |
| inline address stackChunkOopDesc::pc() const { return jdk_internal_vm_StackChunk::pc(as_oop()); } | |
| inline void stackChunkOopDesc::set_pc(address value) { jdk_internal_vm_StackChunk::set_pc(this, value); } | |
| inline uint8_t stackChunkOopDesc::flags() const { return jdk_internal_vm_StackChunk::flags(as_oop()); } | |
| inline void stackChunkOopDesc::set_flags(uint8_t value) { jdk_internal_vm_StackChunk::set_flags(this, value); } | |
| inline uint8_t stackChunkOopDesc::flags_acquire() const { return jdk_internal_vm_StackChunk::flags_acquire(as_oop()); } | |
| inline void stackChunkOopDesc::release_set_flags(uint8_t value) { | |
| jdk_internal_vm_StackChunk::release_set_flags(this, value); | |
| } | |
| inline bool stackChunkOopDesc::try_set_flags(uint8_t prev_flags, uint8_t new_flags) { | |
| return jdk_internal_vm_StackChunk::try_set_flags(this, prev_flags, new_flags); | |
| } | |
| inline int stackChunkOopDesc::max_thawing_size() const { return jdk_internal_vm_StackChunk::maxThawingSize(as_oop()); } | |
| inline void stackChunkOopDesc::set_max_thawing_size(int value) { | |
| assert(value >= 0, "size must be >= 0"); | |
| jdk_internal_vm_StackChunk::set_maxThawingSize(this, (jint)value); | |
| } | |
| inline uint8_t stackChunkOopDesc::lockstack_size() const { return jdk_internal_vm_StackChunk::lockStackSize(as_oop()); } | |
| inline void stackChunkOopDesc::set_lockstack_size(uint8_t value) { jdk_internal_vm_StackChunk::set_lockStackSize(this, value); } | |
| inline oop stackChunkOopDesc::cont() const { return jdk_internal_vm_StackChunk::cont(as_oop()); } | |
| inline void stackChunkOopDesc::set_cont(oop value) { jdk_internal_vm_StackChunk::set_cont(this, value); } | |
| template<typename P> | |
| inline void stackChunkOopDesc::set_cont_raw(oop value) { jdk_internal_vm_StackChunk::set_cont_raw<P>(this, value); } | |
| template<DecoratorSet decorators> | |
| inline void stackChunkOopDesc::set_cont_access(oop value) { jdk_internal_vm_StackChunk::set_cont_access<decorators>(this, value); } | |
| inline int stackChunkOopDesc::argsize() const { | |
| assert(!is_empty(), "should not ask for argsize in empty chunk"); | |
| return stack_size() - bottom() - 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()); | |
| } | |
| inline intptr_t* stackChunkOopDesc::start_address() const { return (intptr_t*)start_of_stack(); } | |
| inline intptr_t* stackChunkOopDesc::end_address() const { return start_address() + stack_size(); } | |
| inline intptr_t* stackChunkOopDesc::bottom_address() const { return start_address() + bottom(); } | |
| inline intptr_t* stackChunkOopDesc::sp_address() const { return start_address() + sp(); } | |
| inline int stackChunkOopDesc::to_offset(intptr_t* p) const { | |
| assert(is_in_chunk(p) | |
| || (p >= start_address() && (p - start_address()) <= stack_size() + frame::metadata_words), | |
| "p: " PTR_FORMAT " start: " PTR_FORMAT " end: " PTR_FORMAT, p2i(p), p2i(start_address()), p2i(bottom_address())); | |
| return (int)(p - start_address()); | |
| } | |
| inline intptr_t* stackChunkOopDesc::from_offset(int offset) const { | |
| assert(offset <= stack_size(), ""); | |
| return start_address() + offset; | |
| } | |
| inline bool stackChunkOopDesc::is_empty() const { | |
| assert(sp() <= bottom(), ""); | |
| return sp() == bottom(); | |
| } | |
| inline bool stackChunkOopDesc::is_in_chunk(void* p) const { | |
| HeapWord* start = (HeapWord*)start_address(); | |
| HeapWord* end = start + stack_size(); | |
| return (HeapWord*)p >= start && (HeapWord*)p < end; | |
| } | |
| bool stackChunkOopDesc::is_usable_in_chunk(void* p) const { | |
| HeapWord* start = (HeapWord*)start_address() + sp() - frame::metadata_words_at_bottom; | |
| HeapWord* end = start + stack_size(); | |
| return (HeapWord*)p >= start && (HeapWord*)p < end; | |
| } | |
| inline bool stackChunkOopDesc::is_flag(uint8_t flag) const { | |
| return (flags() & flag) != 0; | |
| } | |
| inline bool stackChunkOopDesc::is_flag_acquire(uint8_t flag) const { | |
| return (flags_acquire() & flag) != 0; | |
| } | |
| inline void stackChunkOopDesc::set_flag(uint8_t flag, bool value) { | |
| uint32_t flags = this->flags(); | |
| set_flags((uint8_t)(value ? flags |= flag : flags &= ~flag)); | |
| } | |
| inline void stackChunkOopDesc::clear_flags() { | |
| set_flags(0); | |
| } | |
| inline bool stackChunkOopDesc::has_mixed_frames() const { return is_flag(FLAG_HAS_INTERPRETED_FRAMES); } | |
| inline void stackChunkOopDesc::set_has_mixed_frames(bool value) { | |
| assert((flags() & ~(FLAG_HAS_INTERPRETED_FRAMES | FLAG_PREEMPTED)) == 0, "other flags should not be set"); | |
| set_flag(FLAG_HAS_INTERPRETED_FRAMES, value); | |
| } | |
| inline bool stackChunkOopDesc::preempted() const { return is_flag(FLAG_PREEMPTED); } | |
| inline void stackChunkOopDesc::set_preempted(bool value) { | |
| assert(preempted() != value, ""); | |
| set_flag(FLAG_PREEMPTED, value); | |
| } | |
| inline bool stackChunkOopDesc::has_lockstack() const { return is_flag(FLAG_HAS_LOCKSTACK); } | |
| inline void stackChunkOopDesc::set_has_lockstack(bool value) { set_flag(FLAG_HAS_LOCKSTACK, value); } | |
| inline bool stackChunkOopDesc::is_gc_mode() const { return is_flag(FLAG_GC_MODE); } | |
| inline bool stackChunkOopDesc::is_gc_mode_acquire() const { return is_flag_acquire(FLAG_GC_MODE); } | |
| inline void stackChunkOopDesc::set_gc_mode(bool value) { set_flag(FLAG_GC_MODE, value); } | |
| inline bool stackChunkOopDesc::has_bitmap() const { return is_flag(FLAG_HAS_BITMAP); } | |
| inline void stackChunkOopDesc::set_has_bitmap(bool value) { set_flag(FLAG_HAS_BITMAP, value); } | |
| inline bool stackChunkOopDesc::has_thaw_slowpath_condition() const { return flags() != 0; } | |
| inline bool stackChunkOopDesc::requires_barriers() { | |
| return Universe::heap()->requires_barriers(this); | |
| } | |
| template <stackChunkOopDesc::BarrierType barrier, ChunkFrames frame_kind, typename RegisterMapT> | |
| void stackChunkOopDesc::do_barriers(const StackChunkFrameStream<frame_kind>& f, const RegisterMapT* map) { | |
| if (frame_kind == ChunkFrames::Mixed) { | |
| // we could freeze deopted frames in slow mode. | |
| f.handle_deopted(); | |
| } | |
| do_barriers0<barrier>(f, map); | |
| } | |
| template <typename OopT, class StackChunkLockStackClosureType> | |
| inline void stackChunkOopDesc::iterate_lockstack(StackChunkLockStackClosureType* closure) { | |
| assert(LockingMode == LM_LIGHTWEIGHT, ""); | |
| int cnt = lockstack_size(); | |
| intptr_t* lockstart_addr = start_address(); | |
| for (int i = 0; i < cnt; i++) { | |
| closure->do_oop((OopT*)&lockstart_addr[i]); | |
| } | |
| } | |
| template <class StackChunkFrameClosureType> | |
| inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure) { | |
| has_mixed_frames() ? iterate_stack<ChunkFrames::Mixed>(closure) | |
| : iterate_stack<ChunkFrames::CompiledOnly>(closure); | |
| } | |
| template <ChunkFrames frame_kind, class StackChunkFrameClosureType> | |
| inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure) { | |
| const SmallRegisterMap* map = SmallRegisterMap::instance(); | |
| assert(!map->in_cont(), ""); | |
| StackChunkFrameStream<frame_kind> f(this); | |
| bool should_continue = true; | |
| if (f.is_stub()) { | |
| RegisterMap full_map(nullptr, | |
| RegisterMap::UpdateMap::include, | |
| RegisterMap::ProcessFrames::skip, | |
| RegisterMap::WalkContinuation::include); | |
| full_map.set_include_argument_oops(false); | |
| closure->do_frame(f, map); | |
| f.next(&full_map); | |
| assert(!f.is_done(), ""); | |
| assert(f.is_compiled(), ""); | |
| should_continue = closure->do_frame(f, &full_map); | |
| f.next(map); | |
| } | |
| assert(!f.is_stub(), ""); | |
| for(; should_continue && !f.is_done(); f.next(map)) { | |
| if (frame_kind == ChunkFrames::Mixed) { | |
| // in slow mode we might freeze deoptimized frames | |
| f.handle_deopted(); | |
| } | |
| should_continue = closure->do_frame(f, map); | |
| } | |
| } | |
| inline frame stackChunkOopDesc::relativize(frame fr) const { relativize_frame(fr); return fr; } | |
| inline frame stackChunkOopDesc::derelativize(frame fr) const { derelativize_frame(fr); return fr; } | |
| inline void* stackChunkOopDesc::gc_data() const { | |
| int stack_sz = stack_size(); | |
| assert(stack_sz != 0, "stack should not be empty"); | |
| // The gc data is located after the stack. | |
| return start_of_stack() + stack_sz; | |
| } | |
| inline BitMapView stackChunkOopDesc::bitmap() const { | |
| HeapWord* bitmap_addr = static_cast<HeapWord*>(gc_data()); | |
| int stack_sz = stack_size(); | |
| size_t bitmap_size_in_bits = InstanceStackChunkKlass::bitmap_size_in_bits(stack_sz); | |
| BitMapView bitmap((BitMap::bm_word_t*)bitmap_addr, bitmap_size_in_bits); | |
| DEBUG_ONLY(bitmap.verify_range(bit_index_for(start_address()), bit_index_for(end_address()));) | |
| return bitmap; | |
| } | |
| inline BitMap::idx_t stackChunkOopDesc::bit_index_for(address p) const { | |
| return UseCompressedOops ? bit_index_for((narrowOop*)p) : bit_index_for((oop*)p); | |
| } | |
| template <typename OopT> | |
| inline BitMap::idx_t stackChunkOopDesc::bit_index_for(OopT* p) const { | |
| assert(is_aligned(p, alignof(OopT)), "should be aligned: " PTR_FORMAT, p2i(p)); | |
| assert(p >= (OopT*)start_address(), "Address not in chunk"); | |
| return p - (OopT*)start_address(); | |
| } | |
| inline intptr_t* stackChunkOopDesc::address_for_bit(BitMap::idx_t index) const { | |
| return UseCompressedOops ? (intptr_t*)address_for_bit<narrowOop>(index) : (intptr_t*)address_for_bit<oop>(index); | |
| } | |
| template <typename OopT> | |
| inline OopT* stackChunkOopDesc::address_for_bit(BitMap::idx_t index) const { | |
| return (OopT*)start_address() + index; | |
| } | |
| inline MemRegion stackChunkOopDesc::range() { | |
| return MemRegion((HeapWord*)this, size()); | |
| } | |
| inline int stackChunkOopDesc::relativize_usp_offset(const frame& fr, const int usp_offset_in_bytes) const { | |
| assert(fr.is_compiled_frame() || fr.cb()->is_runtime_stub(), ""); | |
| assert(is_in_chunk(fr.unextended_sp()), ""); | |
| intptr_t* base = fr.real_fp(); // equal to the caller's sp | |
| intptr_t* loc = (intptr_t*)((address)fr.unextended_sp() + usp_offset_in_bytes); | |
| assert(base > loc, ""); | |
| return (int)(base - loc); | |
| } | |
| inline address stackChunkOopDesc::usp_offset_to_location(const frame& fr, const int usp_offset_in_bytes) const { | |
| assert(fr.is_compiled_frame(), ""); | |
| return (address)derelativize_address(fr.offset_unextended_sp()) + usp_offset_in_bytes; | |
| } | |
| inline address stackChunkOopDesc::reg_to_location(const frame& fr, const RegisterMap* map, VMReg reg) const { | |
| assert(fr.is_compiled_frame(), ""); | |
| assert(map != nullptr, ""); | |
| assert(map->stack_chunk() == as_oop(), ""); | |
| // the offsets are saved in the map after going through relativize_usp_offset, so they are sp - loc, in words | |
| intptr_t offset = (intptr_t)map->location(reg, nullptr); // see usp_offset_to_index for the chunk case | |
| intptr_t* base = derelativize_address(fr.offset_sp()); | |
| return (address)(base - offset); | |
| } | |
| inline Method* stackChunkOopDesc::interpreter_frame_method(const frame& fr) { | |
| return derelativize(fr).interpreter_frame_method(); | |
| } | |
| inline address stackChunkOopDesc::interpreter_frame_bcp(const frame& fr) { | |
| return derelativize(fr).interpreter_frame_bcp(); | |
| } | |
| inline intptr_t* stackChunkOopDesc::interpreter_frame_expression_stack_at(const frame& fr, int index) const { | |
| frame heap_frame = derelativize(fr); | |
| assert(heap_frame.is_heap_frame(), "must be"); | |
| return heap_frame.interpreter_frame_expression_stack_at(index); | |
| } | |
| inline intptr_t* stackChunkOopDesc::interpreter_frame_local_at(const frame& fr, int index) const { | |
| frame heap_frame = derelativize(fr); | |
| assert(heap_frame.is_heap_frame(), "must be"); | |
| return heap_frame.interpreter_frame_local_at(index); | |
| } | |
| inline void stackChunkOopDesc::copy_from_stack_to_chunk(intptr_t* from, intptr_t* to, int size) { | |
| log_develop_trace(continuations)("Copying from v: " PTR_FORMAT " - " PTR_FORMAT " (%d words, %d bytes)", | |
| p2i(from), p2i(from + size), size, size << LogBytesPerWord); | |
| log_develop_trace(continuations)("Copying to h: " PTR_FORMAT "(" INTPTR_FORMAT "," INTPTR_FORMAT ") - " PTR_FORMAT "(" INTPTR_FORMAT "," INTPTR_FORMAT ") (%d words, %d bytes)", | |
| p2i(to), to - start_address(), relative_base() - to, p2i(to + size), to + size - start_address(), | |
| relative_base() - (to + size), size, size << LogBytesPerWord); | |
| assert(to >= start_address(), "Chunk underflow"); | |
| assert(to + size <= end_address(), "Chunk overflow"); | |
| #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. | |
| if (to != nullptr) | |
| #endif | |
| memcpy(to, from, size << LogBytesPerWord); | |
| } | |
| inline void stackChunkOopDesc::copy_from_chunk_to_stack(intptr_t* from, intptr_t* to, int size) { | |
| log_develop_trace(continuations)("Copying from h: " PTR_FORMAT "(" INTPTR_FORMAT "," INTPTR_FORMAT ") - " PTR_FORMAT "(" INTPTR_FORMAT "," INTPTR_FORMAT ") (%d words, %d bytes)", | |
| p2i(from), from - start_address(), relative_base() - from, p2i(from + size), from + size - start_address(), | |
| relative_base() - (from + size), size, size << LogBytesPerWord); | |
| log_develop_trace(continuations)("Copying to v: " PTR_FORMAT " - " PTR_FORMAT " (%d words, %d bytes)", p2i(to), | |
| p2i(to + size), size, size << LogBytesPerWord); | |
| assert(from >= start_address(), ""); | |
| assert(from + size <= end_address(), ""); | |
| #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. | |
| if (to != nullptr) | |
| #endif | |
| memcpy(to, from, size << LogBytesPerWord); | |
| } | |
| template <typename OopT> | |
| inline oop stackChunkOopDesc::load_oop(OopT* addr) { | |
| return BarrierSet::barrier_set()->barrier_set_stack_chunk()->load_oop(this, addr); | |
| } | |
| inline intptr_t* stackChunkOopDesc::relative_base() const { | |
| // we relativize with respect to end rather than start because GC might compact the chunk | |
| return end_address() + frame::metadata_words; | |
| } | |
| inline intptr_t* stackChunkOopDesc::derelativize_address(int offset) const { | |
| intptr_t* base = relative_base(); | |
| intptr_t* p = base - offset; | |
| assert(start_address() <= p && p <= base, "start_address: " PTR_FORMAT " p: " PTR_FORMAT " base: " PTR_FORMAT, | |
| p2i(start_address()), p2i(p), p2i(base)); | |
| return p; | |
| } | |
| inline int stackChunkOopDesc::relativize_address(intptr_t* p) const { | |
| intptr_t* base = relative_base(); | |
| intptr_t offset = base - p; | |
| assert(start_address() <= p && p <= base, "start_address: " PTR_FORMAT " p: " PTR_FORMAT " base: " PTR_FORMAT, | |
| p2i(start_address()), p2i(p), p2i(base)); | |
| assert(0 <= offset && offset <= std::numeric_limits<int>::max(), "offset: " PTR_FORMAT, offset); | |
| return (int)offset; | |
| } | |
| inline void stackChunkOopDesc::relativize_frame(frame& fr) const { | |
| fr.set_offset_sp(relativize_address(fr.sp())); | |
| fr.set_offset_unextended_sp(relativize_address(fr.unextended_sp())); | |
| relativize_frame_pd(fr); | |
| } | |
| inline void stackChunkOopDesc::derelativize_frame(frame& fr) const { | |
| fr.set_sp(derelativize_address(fr.offset_sp())); | |
| fr.set_unextended_sp(derelativize_address(fr.offset_unextended_sp())); | |
| derelativize_frame_pd(fr); | |
| fr.set_frame_index(-1); // for the sake of assertions in frame | |
| } | |
| #endif // SHARE_OOPS_STACKCHUNKOOP_INLINE_HPP |