| /* |
| * Copyright (c) 2005, 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_UTILITIES_BITMAP_INLINE_HPP |
| #define SHARE_UTILITIES_BITMAP_INLINE_HPP |
| |
| #include "utilities/bitMap.hpp" |
| |
| #include "runtime/atomic.hpp" |
| #include "utilities/align.hpp" |
| #include "utilities/count_trailing_zeros.hpp" |
| #include "utilities/powerOfTwo.hpp" |
| |
| inline void BitMap::set_bit(idx_t bit) { |
| verify_index(bit); |
| *word_addr(bit) |= bit_mask(bit); |
| } |
| |
| inline void BitMap::clear_bit(idx_t bit) { |
| verify_index(bit); |
| *word_addr(bit) &= ~bit_mask(bit); |
| } |
| |
| inline const BitMap::bm_word_t BitMap::load_word_ordered(const volatile bm_word_t* const addr, atomic_memory_order memory_order) { |
| if (memory_order == memory_order_relaxed || memory_order == memory_order_release) { |
| return Atomic::load(addr); |
| } else { |
| assert(memory_order == memory_order_acq_rel || |
| memory_order == memory_order_acquire || |
| memory_order == memory_order_conservative, |
| "unexpected memory ordering"); |
| return Atomic::load_acquire(addr); |
| } |
| } |
| |
| inline bool BitMap::par_at(idx_t bit, atomic_memory_order memory_order) const { |
| verify_index(bit); |
| assert(memory_order == memory_order_acquire || |
| memory_order == memory_order_relaxed, |
| "unexpected memory ordering"); |
| const volatile bm_word_t* const addr = word_addr(bit); |
| return (load_word_ordered(addr, memory_order) & bit_mask(bit)) != 0; |
| } |
| |
| inline bool BitMap::par_set_bit(idx_t bit, atomic_memory_order memory_order) { |
| verify_index(bit); |
| volatile bm_word_t* const addr = word_addr(bit); |
| const bm_word_t mask = bit_mask(bit); |
| bm_word_t old_val = load_word_ordered(addr, memory_order); |
| |
| do { |
| const bm_word_t new_val = old_val | mask; |
| if (new_val == old_val) { |
| return false; // Someone else beat us to it. |
| } |
| const bm_word_t cur_val = Atomic::cmpxchg(addr, old_val, new_val, memory_order); |
| if (cur_val == old_val) { |
| return true; // Success. |
| } |
| old_val = cur_val; // The value changed, try again. |
| } while (true); |
| } |
| |
| inline bool BitMap::par_clear_bit(idx_t bit, atomic_memory_order memory_order) { |
| verify_index(bit); |
| volatile bm_word_t* const addr = word_addr(bit); |
| const bm_word_t mask = ~bit_mask(bit); |
| bm_word_t old_val = load_word_ordered(addr, memory_order); |
| |
| do { |
| const bm_word_t new_val = old_val & mask; |
| if (new_val == old_val) { |
| return false; // Someone else beat us to it. |
| } |
| const bm_word_t cur_val = Atomic::cmpxchg(addr, old_val, new_val, memory_order); |
| if (cur_val == old_val) { |
| return true; // Success. |
| } |
| old_val = cur_val; // The value changed, try again. |
| } while (true); |
| } |
| |
| inline void BitMap::set_range(idx_t beg, idx_t end, RangeSizeHint hint) { |
| if (hint == small_range && end - beg == 1) { |
| set_bit(beg); |
| } else { |
| if (hint == large_range) { |
| set_large_range(beg, end); |
| } else { |
| set_range(beg, end); |
| } |
| } |
| } |
| |
| inline void BitMap::clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { |
| if (end - beg == 1) { |
| clear_bit(beg); |
| } else { |
| if (hint == large_range) { |
| clear_large_range(beg, end); |
| } else { |
| clear_range(beg, end); |
| } |
| } |
| } |
| |
| inline void BitMap::par_set_range(idx_t beg, idx_t end, RangeSizeHint hint) { |
| if (hint == small_range && end - beg == 1) { |
| par_at_put(beg, true); |
| } else { |
| if (hint == large_range) { |
| par_at_put_large_range(beg, end, true); |
| } else { |
| par_at_put_range(beg, end, true); |
| } |
| } |
| } |
| |
| inline void BitMap::set_range_of_words(idx_t beg, idx_t end) { |
| bm_word_t* map = _map; |
| for (idx_t i = beg; i < end; ++i) map[i] = ~(bm_word_t)0; |
| } |
| |
| inline void BitMap::clear_range_of_words(bm_word_t* map, idx_t beg, idx_t end) { |
| for (idx_t i = beg; i < end; ++i) map[i] = 0; |
| } |
| |
| inline void BitMap::clear_range_of_words(idx_t beg, idx_t end) { |
| clear_range_of_words(_map, beg, end); |
| } |
| |
| inline void BitMap::clear() { |
| clear_range_of_words(0, size_in_words()); |
| } |
| |
| inline void BitMap::par_clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { |
| if (hint == small_range && end - beg == 1) { |
| par_at_put(beg, false); |
| } else { |
| if (hint == large_range) { |
| par_at_put_large_range(beg, end, false); |
| } else { |
| par_at_put_range(beg, end, false); |
| } |
| } |
| } |
| |
| // General notes regarding find_{first,last}_bit_impl. |
| // |
| // The first (last) word often contains an interesting bit, either due to |
| // density or because of features of the calling algorithm. So it's important |
| // to examine that word with a minimum of fuss, minimizing setup time for |
| // additional words that will be wasted if the that word is indeed |
| // interesting. |
| // |
| // The first (last) bit is similarly often interesting. When it matters |
| // (density or features of the calling algorithm make it likely that bit is |
| // set), going straight to counting bits compares poorly to examining that bit |
| // first; the counting operations can be relatively expensive, plus there is |
| // the additional range check (unless aligned). But when that bit isn't set, |
| // the cost of having tested for it is relatively small compared to the rest |
| // of the search. |
| // |
| // The benefit from aligned_right being true is relatively small. It saves an |
| // operation in the setup of the word search loop. It also eliminates the |
| // range check on the final result. However, callers often have a comparison |
| // with end, and inlining may allow the two comparisons to be combined. It is |
| // important when !aligned_right that return paths either return end or a |
| // value dominated by a comparison with end. aligned_right is still helpful |
| // when the caller doesn't have a range check because features of the calling |
| // algorithm guarantee an interesting bit will be present. |
| // |
| // The benefit from aligned_left is even smaller, as there is no savings in |
| // the setup of the word search loop. |
| |
| template<BitMap::bm_word_t flip, bool aligned_right> |
| inline BitMap::idx_t BitMap::find_first_bit_impl(idx_t beg, idx_t end) const { |
| STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); |
| verify_range(beg, end); |
| assert(!aligned_right || is_aligned(end, BitsPerWord), "end not aligned"); |
| |
| if (beg < end) { |
| // Get the word containing beg, and shift out low bits. |
| idx_t word_index = to_words_align_down(beg); |
| bm_word_t cword = flipped_word(word_index, flip) >> bit_in_word(beg); |
| if ((cword & 1) != 0) { // Test the beg bit. |
| return beg; |
| } |
| // Position of bit0 of cword in the bitmap. Initially for shifted first word. |
| idx_t cword_pos = beg; |
| if (cword == 0) { // Test other bits in the first word. |
| // First word had no interesting bits. Word search through |
| // aligned up end for a non-zero flipped word. |
| idx_t word_limit = aligned_right |
| ? to_words_align_down(end) // Minuscule savings when aligned. |
| : to_words_align_up(end); |
| while (++word_index < word_limit) { |
| cword = flipped_word(word_index, flip); |
| if (cword != 0) { |
| // Update for found non-zero word, and join common tail to compute |
| // result from cword_pos and non-zero cword. |
| cword_pos = bit_index(word_index); |
| break; |
| } |
| } |
| } |
| // For all paths reaching here, (cword != 0) is already known, so we |
| // expect the compiler to not generate any code for it. Either first word |
| // was non-zero, or found a non-zero word in range, or fully scanned range |
| // (so cword is zero). |
| if (cword != 0) { |
| idx_t result = cword_pos + count_trailing_zeros(cword); |
| if (aligned_right || (result < end)) return result; |
| // Result is beyond range bound; return end. |
| } |
| } |
| return end; |
| } |
| |
| template<BitMap::bm_word_t flip, bool aligned_left> |
| inline BitMap::idx_t BitMap::find_last_bit_impl(idx_t beg, idx_t end) const { |
| STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip); |
| verify_range(beg, end); |
| assert(!aligned_left || is_aligned(beg, BitsPerWord), "beg not aligned"); |
| |
| if (beg < end) { |
| // Get the last partial and flipped word in the range. |
| idx_t last_bit_index = end - 1; |
| idx_t word_index = to_words_align_down(last_bit_index); |
| bm_word_t cword = flipped_word(word_index, flip); |
| // Mask for extracting and testing bits of last word. |
| bm_word_t last_bit_mask = bm_word_t(1) << bit_in_word(last_bit_index); |
| if ((cword & last_bit_mask) != 0) { // Test last bit. |
| return last_bit_index; |
| } |
| // Extract prior bits, clearing those above last_bit_index. |
| cword &= (last_bit_mask - 1); |
| if (cword == 0) { // Test other bits in the last word. |
| // Last word had no interesting bits. Word search through |
| // aligned down beg for a non-zero flipped word. |
| idx_t word_limit = to_words_align_down(beg); |
| while (word_index-- > word_limit) { |
| cword = flipped_word(word_index, flip); |
| if (cword != 0) break; |
| } |
| } |
| // For all paths reaching here, (cword != 0) is already known, so we |
| // expect the compiler to not generate any code for it. Either last word |
| // was non-zero, or found a non-zero word in range, or fully scanned range |
| // (so cword is zero). |
| if (cword != 0) { |
| idx_t result = bit_index(word_index) + log2i(cword); |
| if (aligned_left || (result >= beg)) return result; |
| // Result is below range bound; return end. |
| } |
| } |
| return end; |
| } |
| |
| inline BitMap::idx_t |
| BitMap::find_first_set_bit(idx_t beg, idx_t end) const { |
| return find_first_bit_impl<find_ones_flip, false>(beg, end); |
| } |
| |
| inline BitMap::idx_t |
| BitMap::find_first_clear_bit(idx_t beg, idx_t end) const { |
| return find_first_bit_impl<find_zeros_flip, false>(beg, end); |
| } |
| |
| inline BitMap::idx_t |
| BitMap::find_first_set_bit_aligned_right(idx_t beg, idx_t end) const { |
| return find_first_bit_impl<find_ones_flip, true>(beg, end); |
| } |
| |
| inline BitMap::idx_t |
| BitMap::find_last_set_bit(idx_t beg, idx_t end) const { |
| return find_last_bit_impl<find_ones_flip, false>(beg, end); |
| } |
| |
| inline BitMap::idx_t |
| BitMap::find_last_clear_bit(idx_t beg, idx_t end) const { |
| return find_last_bit_impl<find_zeros_flip, false>(beg, end); |
| } |
| |
| inline BitMap::idx_t |
| BitMap::find_last_set_bit_aligned_left(idx_t beg, idx_t end) const { |
| return find_last_bit_impl<find_ones_flip, true>(beg, end); |
| } |
| |
| // IterateInvoker supports conditionally stopping iteration early. The |
| // invoker is called with the function to apply to each set index, along with |
| // the current index. If the function returns void then the invoker always |
| // returns true, so no early stopping. Otherwise, the result of the function |
| // is returned by the invoker. Iteration stops early if conversion of that |
| // result to bool is false. |
| |
| template<typename ReturnType> |
| struct BitMap::IterateInvoker { |
| template<typename Function> |
| bool operator()(Function function, idx_t index) const { |
| return function(index); // Stop early if converting to bool is false. |
| } |
| }; |
| |
| template<> |
| struct BitMap::IterateInvoker<void> { |
| template<typename Function> |
| bool operator()(Function function, idx_t index) const { |
| function(index); // Result is void. |
| return true; // Never stop early. |
| } |
| }; |
| |
| template <typename Function> |
| inline bool BitMap::iterate(Function function, idx_t beg, idx_t end) const { |
| auto invoke = IterateInvoker<decltype(function(beg))>(); |
| for (idx_t index = beg; true; ++index) { |
| index = find_first_set_bit(index, end); |
| if (index >= end) { |
| return true; |
| } else if (!invoke(function, index)) { |
| return false; |
| } |
| } |
| } |
| |
| template <typename BitMapClosureType> |
| inline bool BitMap::iterate(BitMapClosureType* cl, idx_t beg, idx_t end) const { |
| auto function = [&](idx_t index) { return cl->do_bit(index); }; |
| return iterate(function, beg, end); |
| } |
| |
| template <typename Function> |
| inline bool BitMap::reverse_iterate(Function function, idx_t beg, idx_t end) const { |
| auto invoke = IterateInvoker<decltype(function(beg))>(); |
| for (idx_t index; true; end = index) { |
| index = find_last_set_bit(beg, end); |
| if (index >= end) { |
| return true; |
| } else if (!invoke(function, index)) { |
| return false; |
| } |
| } |
| } |
| |
| template <typename BitMapClosureType> |
| inline bool BitMap::reverse_iterate(BitMapClosureType* cl, idx_t beg, idx_t end) const { |
| auto function = [&](idx_t index) { return cl->do_bit(index); }; |
| return reverse_iterate(function, beg, end); |
| } |
| |
| /// BitMap::IteratorImpl |
| |
| inline BitMap::IteratorImpl::IteratorImpl() |
| : _map(nullptr), _cur_beg(0), _cur_end(0) |
| {} |
| |
| inline BitMap::IteratorImpl::IteratorImpl(const BitMap* map, idx_t beg, idx_t end) |
| : _map(map), _cur_beg(beg), _cur_end(end) |
| {} |
| |
| inline bool BitMap::IteratorImpl::is_empty() const { |
| return _cur_beg == _cur_end; |
| } |
| |
| inline BitMap::idx_t BitMap::IteratorImpl::first() const { |
| assert_not_empty(); |
| return _cur_beg; |
| } |
| |
| inline BitMap::idx_t BitMap::IteratorImpl::last() const { |
| assert_not_empty(); |
| return _cur_end - 1; |
| } |
| |
| inline void BitMap::IteratorImpl::step_first() { |
| assert_not_empty(); |
| _cur_beg = _map->find_first_set_bit(_cur_beg + 1, _cur_end); |
| } |
| |
| inline void BitMap::IteratorImpl::step_last() { |
| assert_not_empty(); |
| idx_t lastpos = last(); |
| idx_t pos = _map->find_last_set_bit(_cur_beg, lastpos); |
| _cur_end = (pos < lastpos) ? (pos + 1) : _cur_beg; |
| } |
| |
| /// BitMap::Iterator |
| |
| inline BitMap::Iterator::Iterator() : _impl() {} |
| |
| inline BitMap::Iterator::Iterator(const BitMap& map) |
| : Iterator(map, 0, map.size()) |
| {} |
| |
| inline BitMap::Iterator::Iterator(const BitMap& map, idx_t beg, idx_t end) |
| : _impl(&map, map.find_first_set_bit(beg, end), end) |
| {} |
| |
| inline bool BitMap::Iterator::is_empty() const { |
| return _impl.is_empty(); |
| } |
| |
| inline BitMap::idx_t BitMap::Iterator::index() const { |
| return _impl.first(); |
| } |
| |
| inline void BitMap::Iterator::step() { |
| _impl.step_first(); |
| } |
| |
| inline BitMap::RBFIterator BitMap::Iterator::begin() const { |
| return RBFIterator(_impl._map, _impl._cur_beg, _impl._cur_end); |
| } |
| |
| inline BitMap::RBFIterator BitMap::Iterator::end() const { |
| return RBFIterator(_impl._map, _impl._cur_end, _impl._cur_end); |
| } |
| |
| /// BitMap::ReverseIterator |
| |
| inline BitMap::idx_t BitMap::ReverseIterator::initial_end(const BitMap& map, |
| idx_t beg, |
| idx_t end) { |
| idx_t pos = map.find_last_set_bit(beg, end); |
| return (pos < end) ? (pos + 1) : beg; |
| } |
| |
| inline BitMap::ReverseIterator::ReverseIterator() : _impl() {} |
| |
| inline BitMap::ReverseIterator::ReverseIterator(const BitMap& map) |
| : ReverseIterator(map, 0, map.size()) |
| {} |
| |
| inline BitMap::ReverseIterator::ReverseIterator(const BitMap& map, |
| idx_t beg, |
| idx_t end) |
| : _impl(&map, beg, initial_end(map, beg, end)) |
| {} |
| |
| inline bool BitMap::ReverseIterator::is_empty() const { |
| return _impl.is_empty(); |
| } |
| |
| inline BitMap::idx_t BitMap::ReverseIterator::index() const { |
| return _impl.last(); |
| } |
| |
| inline void BitMap::ReverseIterator::step() { |
| _impl.step_last(); |
| } |
| |
| inline BitMap::ReverseRBFIterator BitMap::ReverseIterator::begin() const { |
| return ReverseRBFIterator(_impl._map, _impl._cur_beg, _impl._cur_end); |
| } |
| |
| inline BitMap::ReverseRBFIterator BitMap::ReverseIterator::end() const { |
| return ReverseRBFIterator(_impl._map, _impl._cur_beg, _impl._cur_beg); |
| } |
| |
| /// BitMap::RBFIterator |
| |
| inline BitMap::RBFIterator::RBFIterator(const BitMap* map, idx_t beg, idx_t end) |
| : _impl(map, beg, end) |
| {} |
| |
| inline bool BitMap::RBFIterator::operator!=(const RBFIterator& i) const { |
| // Shouldn't be comparing RBF iterators from different contexts. |
| assert(_impl._map == i._impl._map, "mismatched range-based for iterators"); |
| assert(_impl._cur_end == i._impl._cur_end, "mismatched range-based for iterators"); |
| return _impl._cur_beg != i._impl._cur_beg; |
| } |
| |
| inline BitMap::idx_t BitMap::RBFIterator::operator*() const { |
| return _impl.first(); |
| } |
| |
| inline BitMap::RBFIterator& BitMap::RBFIterator::operator++() { |
| _impl.step_first(); |
| return *this; |
| } |
| |
| /// BitMap::ReverseRBFIterator |
| |
| inline BitMap::ReverseRBFIterator::ReverseRBFIterator(const BitMap* map, |
| idx_t beg, |
| idx_t end) |
| : _impl(map, beg, end) |
| {} |
| |
| inline bool BitMap::ReverseRBFIterator::operator!=(const ReverseRBFIterator& i) const { |
| // Shouldn't be comparing RBF iterators from different contexts. |
| assert(_impl._map == i._impl._map, "mismatched range-based for iterators"); |
| assert(_impl._cur_beg == i._impl._cur_beg, "mismatched range-based for iterators"); |
| return _impl._cur_end != i._impl._cur_end; |
| } |
| |
| inline BitMap::idx_t BitMap::ReverseRBFIterator::operator*() const { |
| return _impl.last(); |
| } |
| |
| inline BitMap::ReverseRBFIterator& BitMap::ReverseRBFIterator::operator++() { |
| _impl.step_last(); |
| return *this; |
| } |
| |
| // Returns a bit mask for a range of bits [beg, end) within a single word. Each |
| // bit in the mask is 0 if the bit is in the range, 1 if not in the range. The |
| // returned mask can be used directly to clear the range, or inverted to set the |
| // range. Note: end must not be 0. |
| inline BitMap::bm_word_t |
| BitMap::inverted_bit_mask_for_range(idx_t beg, idx_t end) const { |
| assert(end != 0, "does not work when end == 0"); |
| assert(beg == end || to_words_align_down(beg) == to_words_align_down(end - 1), |
| "must be a single-word range"); |
| bm_word_t mask = bit_mask(beg) - 1; // low (right) bits |
| if (bit_in_word(end) != 0) { |
| mask |= ~(bit_mask(end) - 1); // high (left) bits |
| } |
| return mask; |
| } |
| |
| inline void BitMap::set_large_range_of_words(idx_t beg, idx_t end) { |
| assert(beg <= end, "underflow"); |
| memset(_map + beg, ~(unsigned char)0, (end - beg) * sizeof(bm_word_t)); |
| } |
| |
| inline void BitMap::clear_large_range_of_words(idx_t beg, idx_t end) { |
| assert(beg <= end, "underflow"); |
| memset(_map + beg, 0, (end - beg) * sizeof(bm_word_t)); |
| } |
| |
| inline bool BitMap2D::is_valid_index(idx_t slot_index, idx_t bit_within_slot_index) { |
| verify_bit_within_slot_index(bit_within_slot_index); |
| return (bit_index(slot_index, bit_within_slot_index) < size_in_bits()); |
| } |
| |
| inline bool BitMap2D::at(idx_t slot_index, idx_t bit_within_slot_index) const { |
| verify_bit_within_slot_index(bit_within_slot_index); |
| return _map.at(bit_index(slot_index, bit_within_slot_index)); |
| } |
| |
| inline void BitMap2D::set_bit(idx_t slot_index, idx_t bit_within_slot_index) { |
| verify_bit_within_slot_index(bit_within_slot_index); |
| _map.set_bit(bit_index(slot_index, bit_within_slot_index)); |
| } |
| |
| inline void BitMap2D::clear_bit(idx_t slot_index, idx_t bit_within_slot_index) { |
| verify_bit_within_slot_index(bit_within_slot_index); |
| _map.clear_bit(bit_index(slot_index, bit_within_slot_index)); |
| } |
| |
| inline void BitMap2D::at_put(idx_t slot_index, idx_t bit_within_slot_index, bool value) { |
| verify_bit_within_slot_index(bit_within_slot_index); |
| _map.at_put(bit_index(slot_index, bit_within_slot_index), value); |
| } |
| |
| inline void BitMap2D::at_put_grow(idx_t slot_index, idx_t bit_within_slot_index, bool value) { |
| verify_bit_within_slot_index(bit_within_slot_index); |
| |
| idx_t bit = bit_index(slot_index, bit_within_slot_index); |
| if (bit >= _map.size()) { |
| _map.resize(2 * MAX2(_map.size(), bit)); |
| } |
| _map.at_put(bit, value); |
| } |
| |
| #endif // SHARE_UTILITIES_BITMAP_INLINE_HPP |