Skip to content

Commit 2729d18

Browse files
authored
BE-303: HashQL: Split MIR basic blocks by target support (#8354)
1 parent 11c7cc2 commit 2729d18

File tree

17 files changed

+2958
-141
lines changed

17 files changed

+2958
-141
lines changed

libs/@local/hashql/core/src/id/array.rs

Lines changed: 422 additions & 0 deletions
Large diffs are not rendered by default.

libs/@local/hashql/core/src/id/bit_vec/finite.rs

Lines changed: 687 additions & 0 deletions
Large diffs are not rendered by default.

libs/@local/hashql/core/src/id/bit_vec/mod.rs

Lines changed: 4 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
//! - Implement `MixedBitSet::intersect`.
2828
//! - Implement `DenseBitSet::negate`
2929
//! - Implement `DenseBitSet::first_unset`
30+
//! - Re-implement `FiniteBitSet`
3031
#![expect(
3132
clippy::integer_division,
3233
clippy::integer_division_remainder_used,
@@ -39,14 +40,16 @@ use core::{
3940
alloc::Allocator,
4041
fmt, iter,
4142
marker::PhantomData,
42-
ops::{BitAnd, BitAndAssign, BitOrAssign, Bound, Not, Range, RangeBounds, Shl},
43+
ops::{Bound, Range, RangeBounds},
4344
slice,
4445
};
4546

4647
use smallvec::{SmallVec, smallvec};
4748

49+
pub use self::finite::{FiniteBitIter, FiniteBitSet};
4850
use super::{Id, IdVec};
4951

52+
mod finite;
5053
#[cfg(test)]
5154
mod tests;
5255

@@ -1995,112 +1998,3 @@ const fn max_bit(word: Word) -> usize {
19951998
fn count_ones(words: &[Word]) -> usize {
19961999
words.iter().map(|word| word.count_ones() as usize).sum()
19972000
}
1998-
1999-
/// Integral type used to represent the bit set.
2000-
pub trait FiniteBitSetTy:
2001-
BitAnd<Output = Self>
2002-
+ BitAndAssign
2003-
+ BitOrAssign
2004-
+ Clone
2005-
+ Copy
2006-
+ Shl
2007-
+ Not<Output = Self>
2008-
+ PartialEq
2009-
+ Sized
2010-
{
2011-
/// Size of the domain representable by this type, e.g. 64 for `u64`.
2012-
const DOMAIN_SIZE: u32;
2013-
2014-
/// Value which represents the `FiniteBitSet` having every bit set.
2015-
const FILLED: Self;
2016-
/// Value which represents the `FiniteBitSet` having no bits set.
2017-
const EMPTY: Self;
2018-
2019-
/// Value for one as the integral type.
2020-
const ONE: Self;
2021-
/// Value for zero as the integral type.
2022-
const ZERO: Self;
2023-
2024-
/// Perform a checked left shift on the integral type.
2025-
fn checked_shl(self, rhs: u32) -> Option<Self>;
2026-
/// Perform a checked right shift on the integral type.
2027-
fn checked_shr(self, rhs: u32) -> Option<Self>;
2028-
}
2029-
2030-
impl FiniteBitSetTy for u32 {
2031-
const DOMAIN_SIZE: Self = 32;
2032-
const EMPTY: Self = Self::MIN;
2033-
const FILLED: Self = Self::MAX;
2034-
const ONE: Self = 1_u32;
2035-
const ZERO: Self = 0_u32;
2036-
2037-
fn checked_shl(self, rhs: u32) -> Option<Self> {
2038-
self.checked_shl(rhs)
2039-
}
2040-
2041-
fn checked_shr(self, rhs: u32) -> Option<Self> {
2042-
self.checked_shr(rhs)
2043-
}
2044-
}
2045-
2046-
impl fmt::Debug for FiniteBitSet<u32> {
2047-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2048-
write!(f, "{:032b}", self.0)
2049-
}
2050-
}
2051-
2052-
/// A fixed-sized bitset type represented by an integer type. Indices outwith than the range
2053-
/// representable by `T` are considered set.
2054-
#[derive(Copy, Clone, Eq, PartialEq)]
2055-
pub struct FiniteBitSet<T: FiniteBitSetTy>(pub T);
2056-
2057-
impl<T: FiniteBitSetTy> FiniteBitSet<T> {
2058-
/// Creates a new, empty bitset.
2059-
pub const fn new_empty() -> Self {
2060-
Self(T::EMPTY)
2061-
}
2062-
2063-
/// Sets the `index`th bit.
2064-
pub fn set(&mut self, index: u32) {
2065-
self.0 |= T::ONE.checked_shl(index).unwrap_or(T::ZERO);
2066-
}
2067-
2068-
/// Unsets the `index`th bit.
2069-
pub fn clear(&mut self, index: u32) {
2070-
self.0 &= !T::ONE.checked_shl(index).unwrap_or(T::ZERO);
2071-
}
2072-
2073-
/// Sets the `i`th to `j`th bits.
2074-
pub fn set_range(&mut self, range: Range<u32>) {
2075-
let bits = T::FILLED
2076-
.checked_shl(range.end - range.start)
2077-
.unwrap_or(T::ZERO)
2078-
.not()
2079-
.checked_shl(range.start)
2080-
.unwrap_or(T::ZERO);
2081-
self.0 |= bits;
2082-
}
2083-
2084-
/// Is the set empty?
2085-
pub fn is_empty(&self) -> bool {
2086-
self.0 == T::EMPTY
2087-
}
2088-
2089-
/// Returns the domain size of the bitset.
2090-
#[must_use]
2091-
pub const fn within_domain(index: u32) -> bool {
2092-
index < T::DOMAIN_SIZE
2093-
}
2094-
2095-
/// Returns if the `index`th bit is set.
2096-
pub fn contains(&self, index: u32) -> Option<bool> {
2097-
Self::within_domain(index)
2098-
.then(|| ((self.0.checked_shr(index).unwrap_or(T::ONE)) & T::ONE) == T::ONE)
2099-
}
2100-
}
2101-
2102-
impl<T: FiniteBitSetTy> Default for FiniteBitSet<T> {
2103-
fn default() -> Self {
2104-
Self::new_empty()
2105-
}
2106-
}

libs/@local/hashql/core/src/id/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod array;
12
pub mod bit_vec;
23
mod index;
34
mod slice;
@@ -13,7 +14,9 @@ use core::{
1314

1415
use ::core::sync::atomic;
1516

16-
pub use self::{index::IntoSliceIndex, slice::IdSlice, union_find::IdUnionFind, vec::IdVec};
17+
pub use self::{
18+
array::IdArray, index::IntoSliceIndex, slice::IdSlice, union_find::IdUnionFind, vec::IdVec,
19+
};
1720

1821
/// Represents errors that can occur when converting values to an [`Id`].
1922
///
@@ -258,8 +261,8 @@ macro_rules! newtype {
258261
$crate::id::newtype!(@parse_attrs [$($other)* #[$attr]] [$($step)*] [$($display)*] ; $(#[$($rest)*])* ; $($tail)*);
259262
};
260263

261-
(@parse_attrs [$($other:tt)*] [$($step:tt)*] [$($display:tt)*]; ; $vis:vis struct $name:ident($type:ident is $min:literal..=$max:expr)) => {
262-
$crate::id::newtype!(@impl [$($other)*] [$($step)*] [$($display)*] $vis struct $name($type is $min..=$max));
264+
(@parse_attrs [$($other:tt)*] [$($step:tt)*] [$($display:tt)*]; ; $($tail:tt)*) => {
265+
$crate::id::newtype!(@impl [$($other)*] [$($step)*] [$($display)*] $($tail)*);
263266
};
264267

265268
// Implementation

libs/@local/hashql/core/src/id/slice.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ where
249249
pub fn swap(&mut self, lhs: I, rhs: I) {
250250
self.raw.swap(lhs.as_usize(), rhs.as_usize());
251251
}
252+
253+
#[inline]
254+
pub fn windows<const N: usize>(&self) -> impl ExactSizeIterator<Item = &[T; N]> {
255+
self.raw.array_windows()
256+
}
252257
}
253258

254259
impl<I, T> IdSlice<I, Option<T>>

libs/@local/hashql/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
binary_heap_into_iter_sorted,
2323
clone_from_ref,
2424
const_cmp,
25+
const_ops,
2526
const_trait_impl,
2627
debug_closure_helpers,
2728
extend_one,

libs/@local/hashql/mir/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
string_from_utf8_lossy_owned,
3232
try_trait_v2,
3333
temporary_niche_types,
34-
const_convert
34+
const_convert,
35+
variant_count
3536
)]
3637
#![expect(clippy::indexing_slicing)]
3738
extern crate alloc;

libs/@local/hashql/mir/src/pass/analysis/execution/cost.rs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use hashql_core::id::{Id as _, bit_vec::DenseBitSet};
1515
use crate::{
1616
body::{
1717
Body,
18-
basic_block::BasicBlockSlice,
18+
basic_block::{BasicBlockId, BasicBlockSlice},
1919
basic_blocks::BasicBlocks,
2020
local::{Local, LocalVec},
2121
location::Location,
@@ -140,11 +140,12 @@ pub struct StatementCostVec<A: Allocator = Global> {
140140

141141
impl<A: Allocator> StatementCostVec<A> {
142142
#[expect(unsafe_code)]
143-
fn from_iter(mut iter: impl ExactSizeIterator<Item = u32>, alloc: A) -> Self
144-
where
145-
A: Clone,
146-
{
147-
let mut offsets = Box::new_uninit_slice_in(iter.len() + 1, alloc.clone());
143+
fn offsets(
144+
mut iter: impl ExactSizeIterator<Item = u32>,
145+
alloc: A,
146+
) -> (Box<BasicBlockSlice<u32>, A>, usize) {
147+
// Try to reuse existing offsets if available and of correct length
148+
let mut offsets = Box::new_uninit_slice_in(iter.len() + 1, alloc);
148149

149150
let mut offset = 0_u32;
150151

@@ -161,12 +162,20 @@ impl<A: Allocator> StatementCostVec<A> {
161162
debug_assert!(rest.is_empty());
162163
debug_assert_eq!(iter.len(), 0);
163164

164-
let costs = alloc::vec::from_elem_in(None, offset as usize, alloc);
165-
166165
// SAFETY: We have initialized all elements of the slice.
167166
let offsets = unsafe { offsets.assume_init() };
168167
let offsets = BasicBlockSlice::from_boxed_slice(offsets);
169168

169+
(offsets, offset as usize)
170+
}
171+
172+
fn from_iter(iter: impl ExactSizeIterator<Item = u32>, alloc: A) -> Self
173+
where
174+
A: Clone,
175+
{
176+
let (offsets, length) = Self::offsets(iter, alloc.clone());
177+
let costs = alloc::vec::from_elem_in(None, length, alloc);
178+
170179
Self { offsets, costs }
171180
}
172181

@@ -184,10 +193,34 @@ impl<A: Allocator> StatementCostVec<A> {
184193
)
185194
}
186195

196+
#[expect(clippy::cast_possible_truncation)]
197+
pub fn remap(&mut self, blocks: &BasicBlocks)
198+
where
199+
A: Clone,
200+
{
201+
let alloc = Box::allocator(&self.offsets).clone();
202+
203+
let (offsets, _) = Self::offsets(
204+
blocks.iter().map(|block| block.statements.len() as u32),
205+
alloc,
206+
);
207+
self.offsets = offsets;
208+
}
209+
187210
pub fn all_unassigned(&self) -> bool {
188211
self.costs.iter().all(Option::is_none)
189212
}
190213

214+
pub fn of(&self, block: BasicBlockId) -> &[Option<Cost>] {
215+
let range = (self.offsets[block] as usize)..(self.offsets[block.plus(1)] as usize);
216+
217+
&self.costs[range]
218+
}
219+
220+
pub fn allocator(&self) -> &A {
221+
Box::allocator(&self.offsets)
222+
}
223+
191224
/// Returns the cost at `location`, or `None` if out of bounds or unassigned.
192225
pub fn get(&self, location: Location) -> Option<Cost> {
193226
let range = (self.offsets[location.block] as usize)

libs/@local/hashql/mir/src/pass/analysis/execution/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ macro_rules! cost {
55
}
66

77
mod cost;
8+
pub mod splitting;
89
pub mod statement_placement;
910
pub mod target;
1011

0 commit comments

Comments
 (0)