| /* | |
| * 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; | |
| } |