blob: 4a7f5055146a12fb7e779a677ddc8b3da17bb138 [file]
// 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);
}