From e70112caf86dac4a01739538d3e6f3d163e47642 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 12 Jan 2025 12:44:28 +1100 Subject: [PATCH] Add `DenseBitSet::union_not` This is similar to the existing `union`, except that bits in the RHS are negated before being incorporated into the LHS. Currently only `DenseBitSet` is supported. Supporting other bitset types is possible, but non-trivial, and currently isn't needed. --- compiler/rustc_index/src/bit_set.rs | 30 +++++++++++++++++++++++ compiler/rustc_index/src/bit_set/tests.rs | 26 ++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index d93707b745d..f12df831cb5 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -281,6 +281,24 @@ impl DenseBitSet { } bit_relations_inherent_impls! {} + + /// Sets `self = self | !other`. + /// + /// FIXME: Incorporate this into [`BitRelations`] and fill out + /// implementations for other bitset types, if needed. + pub fn union_not(&mut self, other: &DenseBitSet) { + assert_eq!(self.domain_size, other.domain_size); + + // FIXME(Zalathar): If we were to forcibly _set_ all excess bits before + // the bitwise update, and then clear them again afterwards, we could + // quickly and accurately detect whether the update changed anything. + // But that's only worth doing if there's an actual use-case. + + bitwise(&mut self.words, &other.words, |a, b| a | !b); + // The bitwise update `a | !b` can result in the last word containing + // out-of-domain bits, so we need to clear them. + self.clear_excess_bits(); + } } // dense REL dense @@ -1087,6 +1105,18 @@ impl fmt::Debug for ChunkedBitSet { } } +/// Sets `out_vec[i] = op(out_vec[i], in_vec[i])` for each index `i` in both +/// slices. The slices must have the same length. +/// +/// Returns true if at least one bit in `out_vec` was changed. +/// +/// ## Warning +/// Some bitwise operations (e.g. union-not, xor) can set output bits that were +/// unset in in both inputs. If this happens in the last word/chunk of a bitset, +/// it can cause the bitset to contain out-of-domain values, which need to +/// be cleared with `clear_excess_bits_in_final_word`. This also makes the +/// "changed" return value unreliable, because the change might have only +/// affected excess bits. #[inline] fn bitwise(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool where diff --git a/compiler/rustc_index/src/bit_set/tests.rs b/compiler/rustc_index/src/bit_set/tests.rs index 0350740aa81..eaa4aafe721 100644 --- a/compiler/rustc_index/src/bit_set/tests.rs +++ b/compiler/rustc_index/src/bit_set/tests.rs @@ -75,6 +75,32 @@ fn union_two_sets() { assert!(set1.contains(64)); } +#[test] +fn union_not() { + let mut a = DenseBitSet::::new_empty(100); + let mut b = DenseBitSet::::new_empty(100); + + a.insert(3); + a.insert(5); + a.insert(80); + a.insert(81); + + b.insert(5); // Already in `a`. + b.insert(7); + b.insert(63); + b.insert(81); // Already in `a`. + b.insert(90); + + a.union_not(&b); + + // After union-not, `a` should contain all values in the domain, except for + // the ones that are in `b` and were _not_ already in `a`. + assert_eq!( + a.iter().collect::>(), + (0usize..100).filter(|&x| !matches!(x, 7 | 63 | 90)).collect::>(), + ); +} + #[test] fn chunked_bitset() { let mut b0 = ChunkedBitSet::::new_empty(0);