| /* |
| * Copyright (c) 2021, 2025, 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. |
| */ |
| |
| #include "gc/shared/gc_globals.hpp" |
| #include "gc/z/zAddress.inline.hpp" |
| #include "gc/z/zBarrier.inline.hpp" |
| #include "gc/z/zGeneration.inline.hpp" |
| #include "gc/z/zStoreBarrierBuffer.inline.hpp" |
| #include "gc/z/zUncoloredRoot.inline.hpp" |
| #include "memory/resourceArea.hpp" |
| #include "runtime/threadSMR.hpp" |
| #include "utilities/ostream.hpp" |
| #include "utilities/vmError.hpp" |
| |
| ByteSize ZStoreBarrierEntry::p_offset() { |
| return byte_offset_of(ZStoreBarrierEntry, _p); |
| } |
| |
| ByteSize ZStoreBarrierEntry::prev_offset() { |
| return byte_offset_of(ZStoreBarrierEntry, _prev); |
| } |
| |
| ByteSize ZStoreBarrierBuffer::buffer_offset() { |
| return byte_offset_of(ZStoreBarrierBuffer, _buffer); |
| } |
| |
| ByteSize ZStoreBarrierBuffer::current_offset() { |
| return byte_offset_of(ZStoreBarrierBuffer, _current); |
| } |
| |
| ZStoreBarrierBuffer::ZStoreBarrierBuffer() |
| : _buffer(), |
| _last_processed_color(), |
| _last_installed_color(), |
| _base_pointer_lock(), |
| _base_pointers(), |
| _current(ZBufferStoreBarriers ? BufferSizeBytes : 0) {} |
| |
| void ZStoreBarrierBuffer::initialize() { |
| _last_processed_color = ZPointerStoreGoodMask; |
| _last_installed_color = ZPointerStoreGoodMask; |
| } |
| |
| void ZStoreBarrierBuffer::clear() { |
| _current = BufferSizeBytes; |
| } |
| |
| bool ZStoreBarrierBuffer::is_empty() const { |
| return _current == BufferSizeBytes; |
| } |
| |
| void ZStoreBarrierBuffer::install_base_pointers_inner() { |
| assert(ZPointer::remap_bits(_last_installed_color) == |
| ZPointer::remap_bits(_last_processed_color), |
| "Can't deal with two pending base pointer installations"); |
| |
| assert((ZPointer::remap_bits(_last_processed_color) & ZPointerRemappedYoungMask) == 0 || |
| (ZPointer::remap_bits(_last_processed_color) & ZPointerRemappedOldMask) == 0, |
| "Should not have double bit errors"); |
| |
| for (size_t i = current(); i < BufferLength; ++i) { |
| const ZStoreBarrierEntry& entry = _buffer[i]; |
| volatile zpointer* const p = entry._p; |
| const zaddress_unsafe p_unsafe = to_zaddress_unsafe((uintptr_t)p); |
| |
| // Color with the last processed color |
| const zpointer ptr = ZAddress::color(p_unsafe, _last_processed_color); |
| |
| // Look up the generation that thinks that this pointer is not |
| // load good and check if the page is being relocated. |
| ZGeneration* const remap_generation = ZBarrier::remap_generation(ptr); |
| ZForwarding* const forwarding = remap_generation->forwarding(p_unsafe); |
| if (forwarding != nullptr) { |
| // Page is being relocated |
| ZPage* const page = forwarding->page(); |
| _base_pointers[i] = page->find_base(p); |
| } else { |
| // Page is not being relocated |
| _base_pointers[i] = zaddress_unsafe::null; |
| } |
| } |
| } |
| |
| void ZStoreBarrierBuffer::install_base_pointers() { |
| if (!ZBufferStoreBarriers) { |
| return; |
| } |
| |
| // Use a lock since both the GC and the Java thread race to install the base pointers |
| ZLocker<ZLock> locker(&_base_pointer_lock); |
| |
| const bool should_install_base_pointers = ZPointer::remap_bits(_last_installed_color) != ZPointerRemapped; |
| |
| if (should_install_base_pointers) { |
| install_base_pointers_inner(); |
| } |
| |
| // This is used as a claim mechanism to make sure that we only install the base pointers once |
| _last_installed_color = ZPointerStoreGoodMask; |
| } |
| |
| static volatile zpointer* make_load_good(volatile zpointer* p, zaddress_unsafe p_base, uintptr_t color) { |
| assert(!is_null(p_base), "need base pointer"); |
| |
| // Calculate field offset before p_base is remapped |
| const uintptr_t offset = (uintptr_t)p - untype(p_base); |
| |
| // Remap local-copy of base pointer |
| ZUncoloredRoot::process_no_keepalive(&p_base, color); |
| |
| // Retype now that the address is known to point to the correct address |
| const zaddress p_base_remapped = safe(p_base); |
| |
| assert(offset < ZUtils::object_size(p_base_remapped), |
| "wrong base object; live bits are invalid"); |
| |
| // Calculate remapped field address |
| const zaddress p_remapped = to_zaddress(untype(p_base_remapped) + offset); |
| |
| return (volatile zpointer*)p_remapped; |
| } |
| |
| void ZStoreBarrierBuffer::on_new_phase_relocate(size_t i) { |
| const uintptr_t last_remap_bits = ZPointer::remap_bits(_last_processed_color); |
| if (last_remap_bits == ZPointerRemapped) { |
| // All pointers are already remapped |
| return; |
| } |
| |
| const zaddress_unsafe p_base = _base_pointers[i]; |
| if (is_null(p_base)) { |
| // Page is not part of the relocation set |
| return; |
| } |
| |
| ZStoreBarrierEntry& entry = _buffer[i]; |
| |
| // Relocate the base object and calculate the remapped p |
| entry._p = make_load_good(entry._p, p_base, _last_processed_color); |
| } |
| |
| void ZStoreBarrierBuffer::on_new_phase_remember(size_t i) { |
| volatile zpointer* const p = _buffer[i]._p; |
| |
| if (ZHeap::heap()->is_young(p)) { |
| // Only need remset entries for old objects |
| return; |
| } |
| |
| const uintptr_t last_mark_young_bits = _last_processed_color & (ZPointerMarkedYoung0 | ZPointerMarkedYoung1); |
| const bool woke_up_in_young_mark = last_mark_young_bits != ZPointerMarkedYoung; |
| |
| if (woke_up_in_young_mark) { |
| // When young mark starts we "flip" the remembered sets. The remembered |
| // sets used before the young mark start becomes read-only and used by |
| // the GC to scan for old-to-young pointers to use as marking roots. |
| // |
| // Entries in the store buffer that were added before the mark young start, |
| // were supposed to be part of the remembered sets that the GC scans. |
| // However, it is too late to add those entries at this point, so instead |
| // we perform the GC remembered set scanning up-front here. |
| ZGeneration::young()->scan_remembered_field(p); |
| } else { |
| // The remembered set wasn't flipped in this phase shift, |
| // so just add the remembered set entry. |
| ZGeneration::young()->remember(p); |
| } |
| } |
| |
| bool ZStoreBarrierBuffer::is_old_mark() const { |
| return ZGeneration::old()->is_phase_mark(); |
| } |
| |
| bool ZStoreBarrierBuffer::stored_during_old_mark() const { |
| const uintptr_t last_mark_old_bits = _last_processed_color & (ZPointerMarkedOld0 | ZPointerMarkedOld1); |
| return last_mark_old_bits == ZPointerMarkedOld; |
| } |
| |
| void ZStoreBarrierBuffer::on_new_phase_mark(size_t i) { |
| const ZStoreBarrierEntry& entry = _buffer[i]; |
| const zpointer prev = entry._prev; |
| |
| if (is_null_any(prev)) { |
| return; |
| } |
| |
| volatile zpointer* const p = entry._p; |
| |
| // Young collections can start during old collections, but not the other |
| // way around. Therefore, only old marking can see a collection phase |
| // shift (resulting in a call to this function). |
| // |
| // Stores before the marking phase started is not a part of the SATB snapshot, |
| // and therefore shouldn't be used for marking. |
| // |
| // Locations in the young generation are not part of the old marking. |
| if (is_old_mark() && stored_during_old_mark() && ZHeap::heap()->is_old(p)) { |
| const zaddress addr = ZBarrier::make_load_good(prev); |
| ZUncoloredRoot::mark_object(addr); |
| } |
| } |
| |
| void ZStoreBarrierBuffer::on_new_phase() { |
| if (!ZBufferStoreBarriers) { |
| return; |
| } |
| |
| // Install all base pointers for relocation |
| install_base_pointers(); |
| |
| for (size_t i = current(); i < BufferLength; ++i) { |
| on_new_phase_relocate(i); |
| on_new_phase_remember(i); |
| on_new_phase_mark(i); |
| } |
| |
| clear(); |
| |
| _last_processed_color = ZPointerStoreGoodMask; |
| assert(_last_installed_color == _last_processed_color, "invariant"); |
| } |
| |
| class ZStoreBarrierBuffer::OnError : public VMErrorCallback { |
| private: |
| ZStoreBarrierBuffer* _buffer; |
| |
| public: |
| OnError(ZStoreBarrierBuffer* buffer) |
| : _buffer(buffer) {} |
| |
| virtual void call(outputStream* st) { |
| _buffer->on_error(st); |
| } |
| }; |
| |
| void ZStoreBarrierBuffer::on_error(outputStream* st) { |
| st->print_cr("ZStoreBarrierBuffer: error when flushing"); |
| st->print_cr(" _last_processed_color: " PTR_FORMAT, _last_processed_color); |
| st->print_cr(" _last_installed_color: " PTR_FORMAT, _last_installed_color); |
| |
| for (size_t i = current(); i < BufferLength; ++i) { |
| st->print_cr(" [%2zu]: base: " PTR_FORMAT " p: " PTR_FORMAT " prev: " PTR_FORMAT, |
| i, |
| untype(_base_pointers[i]), |
| p2i(_buffer[i]._p), |
| untype(_buffer[i]._prev)); |
| } |
| } |
| |
| void ZStoreBarrierBuffer::flush() { |
| if (!ZBufferStoreBarriers) { |
| return; |
| } |
| |
| OnError on_error(this); |
| VMErrorCallbackMark mark(&on_error); |
| |
| for (size_t i = current(); i < BufferLength; ++i) { |
| const ZStoreBarrierEntry& entry = _buffer[i]; |
| const zaddress addr = ZBarrier::make_load_good(entry._prev); |
| ZBarrier::mark_and_remember(entry._p, addr); |
| } |
| |
| clear(); |
| } |
| |
| bool ZStoreBarrierBuffer::is_in(volatile zpointer* p) { |
| if (!ZBufferStoreBarriers) { |
| return false; |
| } |
| |
| for (JavaThreadIteratorWithHandle jtiwh; JavaThread * const jt = jtiwh.next(); ) { |
| ZStoreBarrierBuffer* const buffer = ZThreadLocalData::store_barrier_buffer(jt); |
| |
| const uintptr_t last_remap_bits = ZPointer::remap_bits(buffer->_last_processed_color) & ZPointerRemappedMask; |
| const bool needs_remap = last_remap_bits != ZPointerRemapped; |
| |
| for (size_t i = buffer->current(); i < BufferLength; ++i) { |
| const ZStoreBarrierEntry& entry = buffer->_buffer[i]; |
| volatile zpointer* entry_p = entry._p; |
| |
| // Potentially remap p |
| if (needs_remap) { |
| const zaddress_unsafe entry_p_base = buffer->_base_pointers[i]; |
| if (!is_null(entry_p_base)) { |
| entry_p = make_load_good(entry_p, entry_p_base, buffer->_last_processed_color); |
| } |
| } |
| |
| // Check if p matches |
| if (entry_p == p) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |