| /* |
| * Copyright (c) 2021, 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_GC_G1_G1CARDSETCONTAINERS_INLINE_HPP |
| #define SHARE_GC_G1_G1CARDSETCONTAINERS_INLINE_HPP |
| |
| #include "gc/g1/g1CardSetContainers.hpp" |
| #include "gc/g1/g1GCPhaseTimes.hpp" |
| #include "utilities/bitMap.inline.hpp" |
| #include "utilities/globalDefinitions.hpp" |
| #include "utilities/spinYield.hpp" |
| |
| inline G1CardSetInlinePtr::ContainerPtr G1CardSetInlinePtr::merge(ContainerPtr orig_value, uint card_in_region, uint idx, uint bits_per_card) { |
| assert((idx & (SizeFieldMask >> SizeFieldPos)) == idx, "Index %u too large to fit into size field", idx); |
| assert(card_in_region < ((uint)1 << bits_per_card), "Card %u too large to fit into card value field", card_in_region); |
| |
| uint8_t card_pos = card_pos_for(idx, bits_per_card); |
| assert(card_pos + bits_per_card < BitsInValue, "Putting card at pos %u with %u bits would extend beyond pointer", card_pos, bits_per_card); |
| |
| // Check that we do not touch any fields we do not own. |
| uintptr_t mask = ((((uintptr_t)1 << bits_per_card) - 1) << card_pos); |
| assert(((uintptr_t)orig_value & mask) == 0, "The bits in the new range should be empty; orig_value " PTR_FORMAT " mask " PTR_FORMAT, p2i(orig_value), mask); |
| |
| uintptr_t value = ((uintptr_t)(idx + 1) << SizeFieldPos) | ((uintptr_t)card_in_region << card_pos); |
| uintptr_t res = (((uintptr_t)orig_value & ~SizeFieldMask) | value); |
| return (ContainerPtr)res; |
| } |
| |
| inline G1AddCardResult G1CardSetInlinePtr::add(uint card_idx, uint bits_per_card, uint max_cards_in_inline_ptr) { |
| assert(_value_addr != nullptr, "No value address available, cannot add to set."); |
| |
| uint cur_idx = 0; |
| while (true) { |
| uint num_cards = num_cards_in(_value); |
| if (num_cards > 0) { |
| cur_idx = find(card_idx, bits_per_card, cur_idx, num_cards); |
| } |
| // Check if the card is already stored in the pointer. |
| if (cur_idx < num_cards) { |
| return Found; |
| } |
| // Check if there is actually enough space. |
| if (num_cards >= max_cards_in_inline_ptr) { |
| return Overflow; |
| } |
| ContainerPtr new_value = merge(_value, card_idx, num_cards, bits_per_card); |
| ContainerPtr old_value = Atomic::cmpxchg(_value_addr, _value, new_value, memory_order_relaxed); |
| if (_value == old_value) { |
| return Added; |
| } |
| // Update values and retry. |
| _value = old_value; |
| // The value of the pointer may have changed to something different than |
| // an inline card set. Exit then instead of overwriting. |
| if (G1CardSet::container_type(_value) != G1CardSet::ContainerInlinePtr) { |
| return Overflow; |
| } |
| } |
| } |
| |
| inline uint G1CardSetInlinePtr::find(uint card_idx, uint bits_per_card, uint start_at, uint num_cards) { |
| assert(start_at < num_cards, "Precondition!"); |
| |
| uintptr_t const card_mask = (1 << bits_per_card) - 1; |
| uintptr_t value = ((uintptr_t)_value) >> card_pos_for(start_at, bits_per_card); |
| |
| // Check if the card is already stored in the pointer. |
| for (uint cur_idx = start_at; cur_idx < num_cards; cur_idx++) { |
| if ((value & card_mask) == card_idx) { |
| return cur_idx; |
| } |
| value >>= bits_per_card; |
| } |
| return num_cards; |
| } |
| |
| inline bool G1CardSetInlinePtr::contains(uint card_idx, uint bits_per_card) { |
| uint num_cards = num_cards_in(_value); |
| if (num_cards == 0) { |
| return false; |
| } |
| uint cur_idx = find(card_idx, bits_per_card, 0, num_cards); |
| return cur_idx < num_cards; |
| } |
| |
| template <class CardVisitor> |
| inline void G1CardSetInlinePtr::iterate(CardVisitor& found, uint bits_per_card) { |
| uint const num_cards = num_cards_in(_value); |
| uintptr_t const card_mask = (1 << bits_per_card) - 1; |
| |
| uintptr_t value = ((uintptr_t)_value) >> card_pos_for(0, bits_per_card); |
| for (uint cur_idx = 0; cur_idx < num_cards; cur_idx++) { |
| found(value & card_mask); |
| value >>= bits_per_card; |
| } |
| } |
| |
| inline bool G1CardSetContainer::try_increment_refcount() { |
| uintptr_t old_value = refcount(); |
| while (true) { |
| if (old_value < 3 || (old_value & 0x1) == 0) { // reclaimed, reference counts are odd numbers starting at 3 |
| return false; // dead, can't revive. |
| } |
| |
| uintptr_t new_value = old_value + 2; |
| uintptr_t ref_count = Atomic::cmpxchg(&_ref_count, old_value, new_value); |
| if (ref_count == old_value) { |
| return true; |
| } |
| old_value = ref_count; |
| } |
| } |
| |
| inline uintptr_t G1CardSetContainer::decrement_refcount() { |
| uintptr_t old_value = refcount(); |
| assert((old_value & 0x1) != 0 && old_value >= 3, "precondition"); |
| return Atomic::sub(&_ref_count, 2u); |
| } |
| |
| inline G1CardSetArray::G1CardSetArray(uint card_in_region, EntryCountType num_cards) : |
| G1CardSetContainer(), |
| _size(num_cards), |
| _num_entries(1) { |
| assert(_size > 0, "CardSetArray of size 0 not supported."); |
| assert(_size < LockBitMask, "Only support CardSetArray of size %u or smaller.", LockBitMask - 1); |
| _data[0] = card_in_region; |
| } |
| |
| inline G1CardSetArray::G1CardSetArrayLocker::G1CardSetArrayLocker(EntryCountType volatile* num_entries_addr) : |
| _num_entries_addr(num_entries_addr) { |
| SpinYield s; |
| EntryCountType num_entries = Atomic::load(_num_entries_addr) & EntryMask; |
| while (true) { |
| EntryCountType old_value = Atomic::cmpxchg(_num_entries_addr, |
| num_entries, |
| (EntryCountType)(num_entries | LockBitMask)); |
| if (old_value == num_entries) { |
| // Succeeded locking the array. |
| _local_num_entries = num_entries; |
| break; |
| } |
| // Failed. Retry (with the lock bit stripped again). |
| num_entries = old_value & EntryMask; |
| s.wait(); |
| } |
| } |
| |
| inline G1AddCardResult G1CardSetArray::add(uint card_idx) { |
| assert(card_idx < (1u << (sizeof(_data[0]) * BitsPerByte)), |
| "Card index %u does not fit allowed card value range.", card_idx); |
| EntryCountType num_entries = Atomic::load_acquire(&_num_entries) & EntryMask; |
| EntryCountType idx = 0; |
| for (; idx < num_entries; idx++) { |
| if (_data[idx] == card_idx) { |
| return Found; |
| } |
| } |
| |
| // Since we did not find the card, lock. |
| G1CardSetArrayLocker x(&_num_entries); |
| |
| // Reload number of entries from the G1CardSetArrayLocker as it might have changed. |
| // It already read the actual value with the necessary synchronization. |
| num_entries = x.num_entries(); |
| // Look if the cards added while waiting for the lock are the same as our card. |
| for (; idx < num_entries; idx++) { |
| if (_data[idx] == card_idx) { |
| return Found; |
| } |
| } |
| |
| // Check if there is space left. |
| if (num_entries == _size) { |
| return Overflow; |
| } |
| |
| _data[num_entries] = card_idx; |
| |
| x.inc_num_entries(); |
| |
| return Added; |
| } |
| |
| inline bool G1CardSetArray::contains(uint card_idx) { |
| EntryCountType num_entries = Atomic::load_acquire(&_num_entries) & EntryMask; |
| |
| for (EntryCountType idx = 0; idx < num_entries; idx++) { |
| if (_data[idx] == card_idx) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| template <class CardVisitor> |
| void G1CardSetArray::iterate(CardVisitor& found) { |
| EntryCountType num_entries = Atomic::load_acquire(&_num_entries) & EntryMask; |
| for (EntryCountType idx = 0; idx < num_entries; idx++) { |
| found(_data[idx]); |
| } |
| } |
| |
| inline size_t G1CardSetArray::header_size_in_bytes() { |
| return offset_of(G1CardSetArray, _data); |
| } |
| |
| inline G1CardSetBitMap::G1CardSetBitMap(uint card_in_region, uint size_in_bits) : |
| G1CardSetContainer(), _num_bits_set(1) { |
| assert(size_in_bits % (sizeof(_bits[0]) * BitsPerByte) == 0, |
| "Size %u should be aligned to bitmap word size.", size_in_bits); |
| BitMapView bm(_bits, size_in_bits); |
| bm.clear(); |
| bm.set_bit(card_in_region); |
| } |
| |
| inline G1AddCardResult G1CardSetBitMap::add(uint card_idx, size_t threshold, size_t size_in_bits) { |
| BitMapView bm(_bits, size_in_bits); |
| if (_num_bits_set >= threshold) { |
| return bm.at(card_idx) ? Found : Overflow; |
| } |
| if (bm.par_set_bit(card_idx)) { |
| Atomic::inc(&_num_bits_set, memory_order_relaxed); |
| return Added; |
| } |
| return Found; |
| } |
| |
| template <class CardVisitor> |
| inline void G1CardSetBitMap::iterate(CardVisitor& found, size_t size_in_bits, uint offset) { |
| BitMapView bm(_bits, size_in_bits); |
| bm.iterate([&](BitMap::idx_t idx) { found(offset | (uint)idx); }); |
| } |
| |
| inline size_t G1CardSetBitMap::header_size_in_bytes() { |
| return offset_of(G1CardSetBitMap, _bits); |
| } |
| |
| inline G1CardSetHowl::G1CardSetHowl(EntryCountType card_in_region, G1CardSetConfiguration* config) : |
| G1CardSetContainer(), |
| _num_entries((config->max_cards_in_array() + 1)) /* Card Transfer will not increment _num_entries */ { |
| EntryCountType num_buckets = config->num_buckets_in_howl(); |
| EntryCountType bucket = config->howl_bucket_index(card_in_region); |
| for (uint i = 0; i < num_buckets; ++i) { |
| _buckets[i] = G1CardSetInlinePtr(); |
| if (i == bucket) { |
| G1CardSetInlinePtr value(&_buckets[i], _buckets[i]); |
| value.add(card_in_region, config->inline_ptr_bits_per_card(), config->max_cards_in_inline_ptr()); |
| } |
| } |
| } |
| |
| inline bool G1CardSetHowl::contains(uint card_idx, G1CardSetConfiguration* config) { |
| EntryCountType bucket = config->howl_bucket_index(card_idx); |
| ContainerPtr* array_entry = get_container_addr(bucket); |
| ContainerPtr container = Atomic::load_acquire(array_entry); |
| |
| switch (G1CardSet::container_type(container)) { |
| case G1CardSet::ContainerArrayOfCards: { |
| return G1CardSet::container_ptr<G1CardSetArray>(container)->contains(card_idx); |
| } |
| case G1CardSet::ContainerBitMap: { |
| uint card_offset = config->howl_bitmap_offset(card_idx); |
| return G1CardSet::container_ptr<G1CardSetBitMap>(container)->contains(card_offset, config->max_cards_in_howl_bitmap()); |
| } |
| case G1CardSet::ContainerInlinePtr: { |
| G1CardSetInlinePtr ptr(container); |
| return ptr.contains(card_idx, config->inline_ptr_bits_per_card()); |
| } |
| case G1CardSet::ContainerHowl: {// Fullcard set entry |
| assert(container == G1CardSet::FullCardSet, "Must be"); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| template <class CardOrRangeVisitor> |
| inline void G1CardSetHowl::iterate(CardOrRangeVisitor& found, G1CardSetConfiguration* config) { |
| for (uint i = 0; i < config->num_buckets_in_howl(); ++i) { |
| iterate_cardset(_buckets[i], i, found, config); |
| } |
| } |
| |
| template <class ContainerPtrVisitor> |
| inline void G1CardSetHowl::iterate(ContainerPtrVisitor& found, uint num_card_sets) { |
| for (uint i = 0; i < num_card_sets; ++i) { |
| found(&_buckets[i]); |
| } |
| } |
| |
| template <class CardOrRangeVisitor> |
| inline void G1CardSetHowl::iterate_cardset(ContainerPtr const container, uint index, CardOrRangeVisitor& found, G1CardSetConfiguration* config) { |
| switch (G1CardSet::container_type(container)) { |
| case G1CardSet::ContainerInlinePtr: { |
| if (found.start_iterate(G1GCPhaseTimes::MergeRSHowlInline)) { |
| G1CardSetInlinePtr ptr(container); |
| ptr.iterate(found, config->inline_ptr_bits_per_card()); |
| } |
| return; |
| } |
| case G1CardSet::ContainerArrayOfCards: { |
| if (found.start_iterate(G1GCPhaseTimes::MergeRSHowlArrayOfCards)) { |
| G1CardSet::container_ptr<G1CardSetArray>(container)->iterate(found); |
| } |
| return; |
| } |
| case G1CardSet::ContainerBitMap: { |
| if (found.start_iterate(G1GCPhaseTimes::MergeRSHowlBitmap)) { |
| uint offset = index << config->log2_max_cards_in_howl_bitmap(); |
| G1CardSet::container_ptr<G1CardSetBitMap>(container)->iterate(found, config->max_cards_in_howl_bitmap(), offset); |
| } |
| return; |
| } |
| case G1CardSet::ContainerHowl: { // actually FullCardSet |
| assert(container == G1CardSet::FullCardSet, "Must be"); |
| if (found.start_iterate(G1GCPhaseTimes::MergeRSHowlFull)) { |
| uint offset = index << config->log2_max_cards_in_howl_bitmap(); |
| found(offset, config->max_cards_in_howl_bitmap()); |
| } |
| return; |
| } |
| } |
| } |
| |
| inline G1CardSetHowl::EntryCountType G1CardSetHowl::num_buckets(size_t size_in_bits, size_t max_cards_in_array, size_t max_num_buckets) { |
| size_t size_bitmap_bytes = BitMap::calc_size_in_words(size_in_bits) * BytesPerWord; |
| // Ensure that in the worst case arrays consume half the memory size |
| // of storing the entire bitmap |
| size_t max_size_arrays_bytes = size_bitmap_bytes / 2; |
| size_t size_array_bytes = max_cards_in_array * sizeof(G1CardSetArray::EntryDataType); |
| size_t num_arrays = max_size_arrays_bytes / size_array_bytes; |
| // We use shifts and masks for indexing the array. So round down to the next |
| // power of two to not use more than expected memory. |
| num_arrays = round_down_power_of_2(MAX2((size_t)1, MIN2(num_arrays, max_num_buckets))); |
| return (EntryCountType)num_arrays; |
| } |
| |
| inline size_t G1CardSetHowl::header_size_in_bytes() { |
| return offset_of(G1CardSetHowl, _buckets); |
| } |
| |
| #endif // SHARE_GC_G1_G1CARDSETCONTAINERS_INLINE_HPP |