| // Copyright 2025 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "iree/base/bitmap.h" |
| |
| #include "iree/base/internal/math.h" |
| |
| //===----------------------------------------------------------------------===// |
| // iree_bitmap_t |
| //===----------------------------------------------------------------------===// |
| |
| // https://en.wikipedia.org/wiki/Find_first_set |
| #define IREE_FFS_U64(v) ((v) == 0 ? -1 : iree_math_count_trailing_zeros_u64(v)) |
| |
| // Returns a word with the bit at |bit_offset| set. |
| // |
| // Examples: |
| // _BIT_OFFSET_TO_WORD_MASK(0) = 0b00..001 |
| // _BIT_OFFSET_TO_WORD_MASK(1) = 0b00..010 |
| // _BIT_OFFSET_TO_WORD_MASK(2) = 0b00..100 |
| #define _BIT_OFFSET_TO_WORD_MASK(bit_offset) \ |
| (1ull << ((bit_offset) % IREE_BITMAP_BITS_PER_WORD)) |
| |
| // Returns a word index in the bitmap containing the bit at |bit_offset|. |
| // |
| // Examples: |
| // _BIT_OFFSET_TO_WORD_INDEX(0) = 0 |
| // _BIT_OFFSET_TO_WORD_INDEX(64) = 1 |
| // _BIT_OFFSET_TO_WORD_INDEX(127) = 1 |
| // _BIT_OFFSET_TO_WORD_INDEX(128) = 2 |
| #define _BIT_OFFSET_TO_WORD_INDEX(bit_offset) \ |
| ((bit_offset) / IREE_BITMAP_BITS_PER_WORD) |
| |
| // Returns a word mask that includes all valid bits after |bit_offset|. |
| // |
| // Examples: |
| // _BIT_PREFIX_WORD_MASK(0) = 0b11..111 |
| // _BIT_PREFIX_WORD_MASK(1) = 0b11..110 |
| // _BIT_PREFIX_WORD_MASK(2) = 0b11..100 |
| // _BIT_PREFIX_WORD_MASK(3) = 0b11..000 |
| #define _BIT_PREFIX_WORD_MASK(bit_offset) \ |
| (~0ull << ((bit_offset) & (IREE_BITMAP_BITS_PER_WORD - 1))) |
| |
| // Returns a word mask that includes all valid bits up to |bit_offset|. |
| // |
| // Examples: |
| // _BIT_SUFFIX_WORD_MASK(0) = 0b00..000 |
| // _BIT_SUFFIX_WORD_MASK(1) = 0b00..001 |
| // _BIT_SUFFIX_WORD_MASK(2) = 0b00..011 |
| // _BIT_SUFFIX_WORD_MASK(3) = 0b00..111 |
| #define _BIT_SUFFIX_WORD_MASK(bit_offset) \ |
| (~0ull >> (-(bit_offset) & (IREE_BITMAP_BITS_PER_WORD - 1))) |
| |
| // Scan full words first and handle any remaining bits after. |
| bool iree_bitmap_any_set(iree_bitmap_t bitmap) { |
| iree_host_size_t i = 0; |
| for (i = 0; i < bitmap.bit_count / IREE_BITMAP_BITS_PER_WORD; ++i) { |
| if (bitmap.words[i]) return true; |
| } |
| if (bitmap.bit_count % IREE_BITMAP_BITS_PER_WORD) { |
| if (bitmap.words[i] & _BIT_SUFFIX_WORD_MASK(bitmap.bit_count)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool iree_bitmap_none_set(iree_bitmap_t bitmap) { |
| return !iree_bitmap_any_set(bitmap); |
| } |
| |
| iree_host_size_t iree_bitmap_count(iree_bitmap_t bitmap) { |
| iree_host_size_t count = 0; |
| iree_host_size_t full_words = bitmap.bit_count / IREE_BITMAP_BITS_PER_WORD; |
| for (iree_host_size_t i = 0; i < full_words; ++i) { |
| count += iree_math_count_ones_u64(bitmap.words[i]); |
| } |
| // Handle tail bits (mask off bits beyond bit_count). |
| iree_host_size_t tail_bits = bitmap.bit_count % IREE_BITMAP_BITS_PER_WORD; |
| if (tail_bits > 0) { |
| uint64_t tail_mask = (1ull << tail_bits) - 1; |
| count += iree_math_count_ones_u64(bitmap.words[full_words] & tail_mask); |
| } |
| return count; |
| } |
| |
| bool iree_bitmap_test(iree_bitmap_t bitmap, iree_host_size_t bit_index) { |
| return 1ull & (bitmap.words[_BIT_OFFSET_TO_WORD_INDEX(bit_index)] >> |
| (bit_index & (IREE_BITMAP_BITS_PER_WORD - 1))); |
| } |
| |
| void iree_bitmap_set(iree_bitmap_t bitmap, iree_host_size_t bit_index) { |
| const uint64_t word_mask = _BIT_OFFSET_TO_WORD_MASK(bit_index); |
| uint64_t* word_ptr = bitmap.words + _BIT_OFFSET_TO_WORD_INDEX(bit_index); |
| *word_ptr |= word_mask; |
| } |
| |
| void iree_bitmap_set_span(iree_bitmap_t bitmap, iree_host_size_t bit_index, |
| iree_host_size_t bit_length) { |
| const iree_host_size_t bit_end = bit_index + bit_length; |
| |
| // Set from the start of the span to the last full word. |
| int64_t bit_chunk = |
| IREE_BITMAP_BITS_PER_WORD - (bit_index % IREE_BITMAP_BITS_PER_WORD); |
| uint64_t* word_ptr = bitmap.words + _BIT_OFFSET_TO_WORD_INDEX(bit_index); |
| uint64_t word_mask = _BIT_PREFIX_WORD_MASK(bit_index); |
| while ((int64_t)bit_length - bit_chunk >= 0) { |
| *word_ptr |= word_mask; |
| word_mask = ~0ull; |
| bit_length -= bit_chunk; |
| bit_chunk = IREE_BITMAP_BITS_PER_WORD; |
| ++word_ptr; |
| } |
| |
| // Set the suffix bits in the last word (if any). |
| if (bit_length > 0) { |
| word_mask &= _BIT_SUFFIX_WORD_MASK(bit_end); |
| *word_ptr |= word_mask; |
| } |
| } |
| |
| void iree_bitmap_set_all(iree_bitmap_t bitmap) { |
| const iree_host_size_t word_count = |
| iree_bitmap_calculate_words(bitmap.bit_count); |
| if (word_count > 0) { |
| memset(bitmap.words, 0xFF, word_count * sizeof(uint64_t)); |
| } |
| } |
| |
| void iree_bitmap_reset(iree_bitmap_t bitmap, iree_host_size_t bit_index) { |
| const uint64_t word_mask = _BIT_OFFSET_TO_WORD_MASK(bit_index); |
| uint64_t* word_ptr = bitmap.words + _BIT_OFFSET_TO_WORD_INDEX(bit_index); |
| *word_ptr &= ~word_mask; |
| } |
| |
| void iree_bitmap_reset_span(iree_bitmap_t bitmap, iree_host_size_t bit_index, |
| iree_host_size_t bit_length) { |
| const iree_host_size_t bit_end = bit_index + bit_length; |
| |
| // Reset from the start of the span to the last full word. |
| int64_t bit_chunk = |
| IREE_BITMAP_BITS_PER_WORD - (bit_index % IREE_BITMAP_BITS_PER_WORD); |
| uint64_t* word_ptr = bitmap.words + _BIT_OFFSET_TO_WORD_INDEX(bit_index); |
| uint64_t word_mask = _BIT_PREFIX_WORD_MASK(bit_index); |
| while ((int64_t)bit_length - bit_chunk >= 0) { |
| *word_ptr &= ~word_mask; |
| word_mask = ~0ull; |
| bit_length -= bit_chunk; |
| bit_chunk = IREE_BITMAP_BITS_PER_WORD; |
| ++word_ptr; |
| } |
| |
| // Reset the suffix bits in the last word (if any). |
| if (bit_length > 0) { |
| word_mask &= _BIT_SUFFIX_WORD_MASK(bit_end); |
| *word_ptr &= ~word_mask; |
| } |
| } |
| |
| void iree_bitmap_reset_all(iree_bitmap_t bitmap) { |
| const iree_host_size_t word_count = |
| iree_bitmap_calculate_words(bitmap.bit_count); |
| if (word_count > 0) { |
| memset(bitmap.words, 0x00, word_count * sizeof(uint64_t)); |
| } |
| } |
| |
| static iree_host_size_t iree_bitmap_find_next_set_bit( |
| const uint64_t* words, iree_host_size_t bit_count, |
| iree_host_size_t bit_offset) { |
| if (IREE_UNLIKELY(bit_offset >= bit_count)) return bit_count; |
| const uint64_t word_mask = _BIT_PREFIX_WORD_MASK(bit_offset); |
| iree_host_size_t word_index = _BIT_OFFSET_TO_WORD_INDEX(bit_offset); |
| uint64_t word = 0; |
| for (word = words[word_index] & word_mask; !word; word = words[word_index]) { |
| if ((word_index + 1) * IREE_BITMAP_BITS_PER_WORD >= bit_count) { |
| return bit_count; // hit end without finding anything |
| } |
| ++word_index; |
| } |
| return iree_min(word_index * IREE_BITMAP_BITS_PER_WORD + IREE_FFS_U64(word), |
| bit_count); |
| } |
| |
| iree_host_size_t iree_bitmap_find_first_set(iree_bitmap_t bitmap, |
| iree_host_size_t bit_offset) { |
| return iree_bitmap_find_next_set_bit(bitmap.words, bitmap.bit_count, |
| bit_offset); |
| } |
| |
| static iree_host_size_t iree_bitmap_find_next_unset_bit( |
| const uint64_t* words, iree_host_size_t bit_count, |
| iree_host_size_t bit_offset) { |
| if (IREE_UNLIKELY(bit_offset >= bit_count)) return bit_count; |
| const uint64_t word_mask = _BIT_PREFIX_WORD_MASK(bit_offset); |
| iree_host_size_t word_index = _BIT_OFFSET_TO_WORD_INDEX(bit_offset); |
| uint64_t word = 0; |
| for (word = ~words[word_index] & word_mask; !word; |
| word = ~words[word_index]) { |
| if ((word_index + 1) * IREE_BITMAP_BITS_PER_WORD >= bit_count) { |
| return bit_count; // hit end without finding anything |
| } |
| ++word_index; |
| } |
| return iree_min(word_index * IREE_BITMAP_BITS_PER_WORD + IREE_FFS_U64(word), |
| bit_count); |
| } |
| |
| iree_host_size_t iree_bitmap_find_first_unset(iree_bitmap_t bitmap, |
| iree_host_size_t bit_offset) { |
| return iree_bitmap_find_next_unset_bit(bitmap.words, bitmap.bit_count, |
| bit_offset); |
| } |
| |
| iree_host_size_t iree_bitmap_find_first_unset_span( |
| iree_bitmap_t bitmap, iree_host_size_t bit_offset, |
| iree_host_size_t bit_length) { |
| iree_host_size_t bit_index = 0; |
| do { |
| bit_index = iree_bitmap_find_next_unset_bit(bitmap.words, bitmap.bit_count, |
| bit_offset); |
| const iree_host_size_t bit_end = bit_index + bit_length; |
| if (bit_end > bitmap.bit_count) return bitmap.bit_count; |
| const iree_host_size_t next_index = |
| iree_bitmap_find_next_set_bit(bitmap.words, bit_end, bit_index); |
| if (next_index < bit_end) { |
| bit_offset = next_index + 1; |
| continue; // resume from next set bit (as we know there's nothing before) |
| } else { |
| break; // found |
| } |
| } while (true); |
| return iree_min(bit_index, bitmap.bit_count); |
| } |