blob: 6b23f074ed6f1bcc8e5abbd8b57266d0ba18fb5b [file] [log] [blame]
/*
* Copyright (c) 2023, 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_HPP
#define SHARE_GC_G1_G1CARDSETCONTAINERS_HPP
#include "gc/g1/g1CardSet.hpp"
#include "memory/allocation.hpp"
#include "runtime/atomic.hpp"
#include "utilities/bitMap.hpp"
#include "utilities/globalDefinitions.hpp"
// A helper class to encode a few card indexes within a ContainerPtr.
//
// The pointer value (either 32 or 64 bits) is split into two areas:
//
// - Header containing identifying tag and number of encoded cards.
// - Data area containing the card indexes themselves
//
// The header starts (from LSB) with the identifying tag (two bits,
// always 00), and three bits size. The size stores the number of
// valid card indexes after the header.
//
// The data area makes up the remainder of the word, with card indexes
// put one after another at increasing bit positions. The separate
// card indexes use just enough space (bits) to represent the whole
// range of cards needed for covering the whole range of values
// (typically in a region). There may be unused space at the top of
// the word.
//
// Example:
//
// 64 bit pointer size, with 8M-size regions (8M == 2^23)
// -> 2^14 (2^23 / 2^9) cards; each card represents 512 bytes in a region
// -> 14 bits per card; must have enough bits to hold the max card index
// -> may encode up to 4 cards into it, using 61 bits (5 bits header + 4 * 14)
//
// M L
// S S
// B B
// +------+ +---------------+--------------+-----+
// |unused| ... | card_index1 | card_index0 |SSS00|
// +------+ +---------------+--------------+-----+
class G1CardSetInlinePtr : public StackObj {
friend class G1CardSetContainersTest;
using ContainerPtr = G1CardSet::ContainerPtr;
ContainerPtr volatile * _value_addr;
ContainerPtr _value;
static const uint SizeFieldLen = 3;
static const uint SizeFieldPos = 2;
static const uint HeaderSize = G1CardSet::ContainerPtrHeaderSize + SizeFieldLen;
static const uint BitsInValue = sizeof(ContainerPtr) * BitsPerByte;
static const uintptr_t SizeFieldMask = (((uint)1 << SizeFieldLen) - 1) << SizeFieldPos;
static uint8_t card_pos_for(uint const idx, uint const bits_per_card) {
return (idx * bits_per_card + HeaderSize);
}
static ContainerPtr merge(ContainerPtr orig_value, uint card_in_region, uint idx, uint bits_per_card);
static uint card_at(ContainerPtr value, uint const idx, uint const bits_per_card) {
uint8_t card_pos = card_pos_for(idx, bits_per_card);
uint result = ((uintptr_t)value >> card_pos) & (((uintptr_t)1 << bits_per_card) - 1);
return result;
}
uint find(uint const card_idx, uint const bits_per_card, uint start_at, uint num_cards);
public:
G1CardSetInlinePtr() : _value_addr(nullptr), _value((ContainerPtr)G1CardSet::ContainerInlinePtr) { }
G1CardSetInlinePtr(ContainerPtr value) : _value_addr(nullptr), _value(value) {
assert(G1CardSet::container_type(_value) == G1CardSet::ContainerInlinePtr, "Value " PTR_FORMAT " is not a valid G1CardSetInlinePtr.", p2i(_value));
}
G1CardSetInlinePtr(ContainerPtr volatile* value_addr, ContainerPtr value) : _value_addr(value_addr), _value(value) {
assert(G1CardSet::container_type(_value) == G1CardSet::ContainerInlinePtr, "Value " PTR_FORMAT " is not a valid G1CardSetInlinePtr.", p2i(_value));
}
G1AddCardResult add(uint const card_idx, uint const bits_per_card, uint const max_cards_in_inline_ptr);
bool contains(uint const card_idx, uint const bits_per_card);
template <class CardVisitor>
void iterate(CardVisitor& found, uint const bits_per_card);
operator ContainerPtr () { return _value; }
static uint max_cards_in_inline_ptr(uint bits_per_card) {
return (BitsInValue - HeaderSize) / bits_per_card;
}
static uint num_cards_in(ContainerPtr value) {
return ((uintptr_t)value & SizeFieldMask) >> SizeFieldPos;
}
};
// Common base class for card set containers where the memory for the entries is
// managed on the (C-)heap. Depending on the current use, one of the two overlapping
// members are used:
//
// While such an object is assigned to a card set container, we utilize the
// reference count for memory management.
//
// In this case the object is one of three states:
// 1: Live: The object is visible to other threads, thus can
// safely be accessed by other threads (_ref_count >= 3).
// 2: Dead: The object is visible to only a single thread and may be
// safely reclaimed (_ref_count == 1).
// 3: Reclaimed: The object's memory has been reclaimed ((_ref_count & 0x1) == 0).
// To maintain these constraints, live objects should have ((_ref_count & 0x1) == 1),
// which requires that we increment the reference counts by 2 starting at _ref_count = 3.
//
// All but inline pointers are of this kind. For those, card entries are stored
// directly in the ContainerPtr of the ConcurrentHashTable node.
class G1CardSetContainer {
uintptr_t _ref_count;
protected:
~G1CardSetContainer() = default;
public:
G1CardSetContainer() : _ref_count(3) { }
uintptr_t refcount() const { return Atomic::load_acquire(&_ref_count); }
bool try_increment_refcount();
// Decrement refcount potentially while racing increment, so we need
// to check the value after attempting to decrement.
uintptr_t decrement_refcount();
// Log of largest card index that can be stored in any G1CardSetContainer
static uint LogCardsPerRegionLimit;
static uint cards_per_region_limit() { return 1u << LogCardsPerRegionLimit; }
};
class G1CardSetArray : public G1CardSetContainer {
public:
typedef uint16_t EntryDataType;
typedef uint EntryCountType;
using ContainerPtr = G1CardSet::ContainerPtr;
private:
EntryCountType _size;
EntryCountType volatile _num_entries;
EntryDataType _data[2];
static const EntryCountType LockBitMask = (EntryCountType)1 << (sizeof(EntryCountType) * BitsPerByte - 1);
static const EntryCountType EntryMask = LockBitMask - 1;
class G1CardSetArrayLocker : public StackObj {
EntryCountType volatile* _num_entries_addr;
EntryCountType _local_num_entries;
public:
G1CardSetArrayLocker(EntryCountType volatile* value);
EntryCountType num_entries() const { return _local_num_entries; }
void inc_num_entries() {
assert(((_local_num_entries + 1) & EntryMask) == (EntryCountType)(_local_num_entries + 1), "no overflow" );
_local_num_entries++;
}
~G1CardSetArrayLocker() {
Atomic::release_store(_num_entries_addr, _local_num_entries);
}
};
public:
G1CardSetArray(uint const card_in_region, EntryCountType num_cards);
G1AddCardResult add(uint card_idx);
bool contains(uint card_idx);
template <class CardVisitor>
void iterate(CardVisitor& found);
size_t num_entries() const { return _num_entries & EntryMask; }
static size_t header_size_in_bytes();
static size_t size_in_bytes(size_t num_cards) {
return header_size_in_bytes() + sizeof(EntryDataType) * num_cards;
}
};
class G1CardSetBitMap : public G1CardSetContainer {
size_t _num_bits_set;
BitMap::bm_word_t _bits[1];
public:
G1CardSetBitMap(uint const card_in_region, uint const size_in_bits);
G1AddCardResult add(uint card_idx, size_t threshold, size_t size_in_bits);
bool contains(uint card_idx, size_t size_in_bits) {
BitMapView bm(_bits, size_in_bits);
return bm.at(card_idx);
}
uint num_bits_set() const { return (uint)_num_bits_set; }
template <class CardVisitor>
void iterate(CardVisitor& found, size_t const size_in_bits, uint offset);
uint next(uint const idx, size_t const size_in_bits) {
BitMapView bm(_bits, size_in_bits);
return static_cast<uint>(bm.find_first_set_bit(idx));
}
static size_t header_size_in_bytes();
static size_t size_in_bytes(size_t size_in_bits) { return header_size_in_bytes() + BitMap::calc_size_in_words(size_in_bits) * BytesPerWord; }
};
class G1CardSetHowl : public G1CardSetContainer {
public:
typedef uint EntryCountType;
using ContainerPtr = G1CardSet::ContainerPtr;
EntryCountType volatile _num_entries;
private:
ContainerPtr _buckets[2];
// Do not add class member variables beyond this point
// Iterates over the given ContainerPtr with at index in this Howl card set,
// applying a CardOrRangeVisitor on it.
template <class CardOrRangeVisitor>
void iterate_cardset(ContainerPtr const container, uint index, CardOrRangeVisitor& found, G1CardSetConfiguration* config);
public:
G1CardSetHowl(EntryCountType card_in_region, G1CardSetConfiguration* config);
ContainerPtr* get_container_addr(EntryCountType index) {
return &_buckets[index];
}
bool contains(uint card_idx, G1CardSetConfiguration* config);
// Iterates over all ContainerPtrs in this Howl card set, applying a CardOrRangeVisitor
// on it.
template <class CardOrRangeVisitor>
void iterate(CardOrRangeVisitor& found, G1CardSetConfiguration* config);
// Iterates over all ContainerPtrs in this Howl card set. Calls
//
// void operator ()(ContainerPtr* card_set_addr);
//
// on all of them.
template <class ContainerPtrVisitor>
void iterate(ContainerPtrVisitor& found, uint num_card_sets);
static EntryCountType num_buckets(size_t size_in_bits, size_t num_cards_in_array, size_t max_buckets);
static EntryCountType bitmap_size(size_t size_in_bits, uint num_buckets) {
EntryCountType num_cards = (EntryCountType)size_in_bits / num_buckets;
return round_up_power_of_2(num_cards);
}
static size_t header_size_in_bytes();
static size_t size_in_bytes(size_t num_arrays) {
return header_size_in_bytes() + sizeof(ContainerPtr) * num_arrays;
}
};
#endif // SHARE_GC_G1_G1CARDSETCONTAINERS_HPP