From: Jacob Lifshay Date: Thu, 9 Feb 2023 01:09:51 +0000 (-0800) Subject: wip X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a860658ac00d60d2956b40cadd024f5043ec9ab9;p=bigint-presentation-code.git wip --- diff --git a/register_allocator/src/function.rs b/register_allocator/src/function.rs index 02419e5..24b73c0 100644 --- a/register_allocator/src/function.rs +++ b/register_allocator/src/function.rs @@ -1,10 +1,8 @@ use crate::{ - error::{ - BlockIdxOutOfRange, BlockParamIdxOutOfRange, Error, InstIdxOutOfRange, - OperandIdxOutOfRange, Result, SSAValIdxOutOfRange, - }, + error::{BlockParamIdxOutOfRange, Error, OperandIdxOutOfRange, Result}, index::{ - BlockIdx, BlockParamIdx, IndexTy, InstIdx, InstRange, OperandIdx, RangeIter, SSAValIdx, + BlockIdx, BlockParamIdx, Entries, InstIdx, InstRange, OperandIdx, SSAValIdx, TryIndex, + TryIndexMut, }, interned::{GlobalState, Intern, Interned}, loc::{BaseTy, Loc, Ty}, @@ -23,9 +21,7 @@ use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use std::{ collections::{btree_map, BTreeMap, BTreeSet}, - iter::FusedIterator, mem, - ops::{Index, IndexMut}, }; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] @@ -182,6 +178,12 @@ impl ProgPoint { pub const fn prev(self) -> Self { Self(self.0 - 1) } + pub const fn as_usize(self) -> usize { + self.0 + } + pub const fn from_usize(value: usize) -> Self { + Self(value) + } } impl fmt::Debug for ProgPoint { @@ -1031,270 +1033,6 @@ impl FnFields { } } -pub trait Entries<'a, I>: Index -where - I: IndexTy, - Self::Output: 'a, -{ - type Iter: Iterator - + DoubleEndedIterator - + ExactSizeIterator - + FusedIterator; - fn entries(&'a self) -> Self::Iter; - fn keys(&'a self) -> RangeIter; -} - -pub trait EntriesMut<'a, I>: Entries<'a, I> + IndexMut -where - I: IndexTy, - Self::Output: 'a, -{ - type IterMut: Iterator - + DoubleEndedIterator - + ExactSizeIterator - + FusedIterator; - fn entries_mut(&'a mut self) -> Self::IterMut; -} - -pub trait TryIndex: for<'a> Entries<'a, I> -where - I: IndexTy, -{ - type Error; - fn try_index(&self, idx: I) -> Result<&Self::Output, Self::Error>; -} - -pub trait TryIndexMut: TryIndex + for<'a> EntriesMut<'a, I> -where - I: IndexTy, -{ - fn try_index_mut(&mut self, idx: I) -> Result<&mut Self::Output, Self::Error>; -} - -macro_rules! impl_index { - ( - #[error = $Error:ident, iter = $Iter:ident, iter_mut = $IterMut:ident] - impl Index<$I:ty> for Vec<$T:ty> {} - ) => { - #[derive(Clone, Debug)] - pub struct $Iter<'a> { - iter: std::iter::Enumerate>, - } - - impl<'a> Iterator for $Iter<'a> { - type Item = ($I, &'a $T); - - fn next(&mut self) -> Option { - self.iter.next().map(|(i, v)| (<$I>::new(i), v)) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn fold(self, init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.iter - .fold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) - } - } - - impl DoubleEndedIterator for $Iter<'_> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|(i, v)| (<$I>::new(i), v)) - } - - fn rfold(self, init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.iter - .rfold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) - } - } - - impl ExactSizeIterator for $Iter<'_> { - fn len(&self) -> usize { - self.iter.len() - } - } - - impl FusedIterator for $Iter<'_> {} - - #[derive(Debug)] - pub struct $IterMut<'a> { - iter: std::iter::Enumerate>, - } - - impl<'a> Iterator for $IterMut<'a> { - type Item = ($I, &'a mut $T); - - fn next(&mut self) -> Option { - self.iter.next().map(|(i, v)| (<$I>::new(i), v)) - } - - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - fn fold(self, init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.iter - .fold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) - } - } - - impl DoubleEndedIterator for $IterMut<'_> { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|(i, v)| (<$I>::new(i), v)) - } - - fn rfold(self, init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - self.iter - .rfold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) - } - } - - impl ExactSizeIterator for $IterMut<'_> { - fn len(&self) -> usize { - self.iter.len() - } - } - - impl FusedIterator for $IterMut<'_> {} - - impl Index<$I> for Vec<$T> { - type Output = $T; - - fn index(&self, index: $I) -> &Self::Output { - &self[index.get()] - } - } - - impl IndexMut<$I> for Vec<$T> { - fn index_mut(&mut self, index: $I) -> &mut Self::Output { - &mut self[index.get()] - } - } - - impl<'a> Entries<'a, $I> for Vec<$T> { - type Iter = $Iter<'a>; - fn entries(&'a self) -> Self::Iter { - $Iter { - iter: (**self).iter().enumerate(), - } - } - fn keys(&'a self) -> RangeIter<$I> { - RangeIter::from_usize_range(0..self.len()) - } - } - - impl<'a> EntriesMut<'a, $I> for Vec<$T> { - type IterMut = $IterMut<'a>; - fn entries_mut(&'a mut self) -> Self::IterMut { - $IterMut { - iter: (**self).iter_mut().enumerate(), - } - } - } - - impl TryIndex<$I> for Vec<$T> { - type Error = $Error; - - fn try_index(&self, idx: $I) -> Result<&Self::Output, Self::Error> { - self.get(idx.get()).ok_or($Error { idx }) - } - } - - impl TryIndexMut<$I> for Vec<$T> { - fn try_index_mut(&mut self, idx: $I) -> Result<&mut Self::Output, Self::Error> { - self.get_mut(idx.get()).ok_or($Error { idx }) - } - } - - impl Index<$I> for [$T] { - type Output = $T; - - fn index(&self, index: $I) -> &Self::Output { - &self[index.get()] - } - } - - impl IndexMut<$I> for [$T] { - fn index_mut(&mut self, index: $I) -> &mut Self::Output { - &mut self[index.get()] - } - } - - impl<'a> Entries<'a, $I> for [$T] { - type Iter = $Iter<'a>; - fn entries(&'a self) -> Self::Iter { - $Iter { - iter: self.iter().enumerate(), - } - } - fn keys(&'a self) -> RangeIter<$I> { - RangeIter::from_usize_range(0..self.len()) - } - } - - impl<'a> EntriesMut<'a, $I> for [$T] { - type IterMut = $IterMut<'a>; - fn entries_mut(&'a mut self) -> Self::IterMut { - $IterMut { - iter: self.iter_mut().enumerate(), - } - } - } - - impl TryIndex<$I> for [$T] { - type Error = $Error; - - fn try_index(&self, idx: $I) -> Result<&Self::Output, Self::Error> { - self.get(idx.get()).ok_or($Error { idx }) - } - } - - impl TryIndexMut<$I> for [$T] { - fn try_index_mut(&mut self, idx: $I) -> Result<&mut Self::Output, Self::Error> { - self.get_mut(idx.get()).ok_or($Error { idx }) - } - } - }; -} - -impl_index! { - #[error = SSAValIdxOutOfRange, iter = SSAValEntriesIter, iter_mut = SSAValEntriesIterMut] - impl Index for Vec {} -} - -impl_index! { - #[error = BlockParamIdxOutOfRange, iter = BlockParamEntriesIter, iter_mut = BlockParamEntriesIterMut] - impl Index for Vec {} -} - -impl_index! { - #[error = OperandIdxOutOfRange, iter = OperandEntriesIter, iter_mut = OperandEntriesIterMut] - impl Index for Vec {} -} - -impl_index! { - #[error = InstIdxOutOfRange, iter = InstEntriesIter, iter_mut = InstEntriesIterMut] - impl Index for Vec {} -} - -impl_index! { - #[error = BlockIdxOutOfRange, iter = BlockEntriesIter, iter_mut = BlockEntriesIterMut] - impl Index for Vec {} -} - impl GraphBase for FnFields { type EdgeId = (BlockIdx, BlockIdx); type NodeId = BlockIdx; diff --git a/register_allocator/src/index.rs b/register_allocator/src/index.rs index a235f4f..bcfd26e 100644 --- a/register_allocator/src/index.rs +++ b/register_allocator/src/index.rs @@ -1,5 +1,19 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::{fmt, hash::Hash, iter::FusedIterator, ops::Range}; +use std::{ + fmt, + hash::Hash, + iter::FusedIterator, + ops::{Index, IndexMut, Range}, +}; + +use crate::{ + error::{ + BlockIdxOutOfRange, BlockParamIdxOutOfRange, InstIdxOutOfRange, OperandIdxOutOfRange, + SSAValIdxOutOfRange, + }, + function::{Block, Inst, Operand, SSAVal}, + live_range::LiveRange, +}; pub trait DisplayOptionIdx { type Type: fmt::Display; @@ -135,12 +149,13 @@ macro_rules! define_index { impl fmt::Display for DisplayOptionIdxImpl { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(v) => v.fmt(f), + match &self.0 { + Some(v) => fmt::Display::fmt(v, f), None => write!(f, "none"), } } } + impl DisplayOptionIdx for Option<$name> { type Type = DisplayOptionIdxImpl; @@ -157,6 +172,280 @@ define_index!(InstIdx); define_index!(BlockIdx); define_index!(BlockParamIdx); define_index!(OperandIdx); +define_index!(LiveRangeIdx); + +pub trait Entries<'a, I>: Index +where + I: IndexTy, + Self::Output: 'a, +{ + type Iter: Iterator + + DoubleEndedIterator + + ExactSizeIterator + + FusedIterator; + fn entries(&'a self) -> Self::Iter; + fn keys(&'a self) -> RangeIter; +} + +pub trait EntriesMut<'a, I>: Entries<'a, I> + IndexMut +where + I: IndexTy, + Self::Output: 'a, +{ + type IterMut: Iterator + + DoubleEndedIterator + + ExactSizeIterator + + FusedIterator; + fn entries_mut(&'a mut self) -> Self::IterMut; +} + +pub trait TryIndex: for<'a> Entries<'a, I> +where + I: IndexTy, +{ + type Error; + fn try_index(&self, idx: I) -> Result<&Self::Output, Self::Error>; +} + +pub trait TryIndexMut: TryIndex + for<'a> EntriesMut<'a, I> +where + I: IndexTy, +{ + fn try_index_mut(&mut self, idx: I) -> Result<&mut Self::Output, Self::Error>; +} + +macro_rules! impl_index { + ( + #[$(error = $Error:ident, )?iter = $Iter:ident, iter_mut = $IterMut:ident] + impl Index<$I:ty> for Vec<$T:ty> {} + ) => { + #[derive(Clone, Debug)] + pub struct $Iter<'a> { + iter: std::iter::Enumerate>, + } + + impl<'a> Iterator for $Iter<'a> { + type Item = ($I, &'a $T); + + fn next(&mut self) -> Option { + self.iter.next().map(|(i, v)| (<$I>::new(i), v)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter + .fold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) + } + } + + impl DoubleEndedIterator for $Iter<'_> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|(i, v)| (<$I>::new(i), v)) + } + + fn rfold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter + .rfold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) + } + } + + impl ExactSizeIterator for $Iter<'_> { + fn len(&self) -> usize { + self.iter.len() + } + } + + impl FusedIterator for $Iter<'_> {} + + #[derive(Debug)] + pub struct $IterMut<'a> { + iter: std::iter::Enumerate>, + } + + impl<'a> Iterator for $IterMut<'a> { + type Item = ($I, &'a mut $T); + + fn next(&mut self) -> Option { + self.iter.next().map(|(i, v)| (<$I>::new(i), v)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter + .fold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) + } + } + + impl DoubleEndedIterator for $IterMut<'_> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|(i, v)| (<$I>::new(i), v)) + } + + fn rfold(self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.iter + .rfold(init, move |a, (i, v)| f(a, (<$I>::new(i), v))) + } + } + + impl ExactSizeIterator for $IterMut<'_> { + fn len(&self) -> usize { + self.iter.len() + } + } + + impl FusedIterator for $IterMut<'_> {} + + impl Index<$I> for Vec<$T> { + type Output = $T; + + fn index(&self, index: $I) -> &Self::Output { + &self[index.get()] + } + } + + impl IndexMut<$I> for Vec<$T> { + fn index_mut(&mut self, index: $I) -> &mut Self::Output { + &mut self[index.get()] + } + } + + impl<'a> Entries<'a, $I> for Vec<$T> { + type Iter = $Iter<'a>; + fn entries(&'a self) -> Self::Iter { + $Iter { + iter: (**self).iter().enumerate(), + } + } + fn keys(&'a self) -> RangeIter<$I> { + RangeIter::from_usize_range(0..self.len()) + } + } + + impl<'a> EntriesMut<'a, $I> for Vec<$T> { + type IterMut = $IterMut<'a>; + fn entries_mut(&'a mut self) -> Self::IterMut { + $IterMut { + iter: (**self).iter_mut().enumerate(), + } + } + } + + $( + impl TryIndex<$I> for Vec<$T> { + type Error = $Error; + + fn try_index(&self, idx: $I) -> Result<&Self::Output, Self::Error> { + self.get(idx.get()).ok_or($Error { idx }) + } + } + + impl TryIndexMut<$I> for Vec<$T> { + fn try_index_mut(&mut self, idx: $I) -> Result<&mut Self::Output, Self::Error> { + self.get_mut(idx.get()).ok_or($Error { idx }) + } + } + )? + + impl Index<$I> for [$T] { + type Output = $T; + + fn index(&self, index: $I) -> &Self::Output { + &self[index.get()] + } + } + + impl IndexMut<$I> for [$T] { + fn index_mut(&mut self, index: $I) -> &mut Self::Output { + &mut self[index.get()] + } + } + + impl<'a> Entries<'a, $I> for [$T] { + type Iter = $Iter<'a>; + fn entries(&'a self) -> Self::Iter { + $Iter { + iter: self.iter().enumerate(), + } + } + fn keys(&'a self) -> RangeIter<$I> { + RangeIter::from_usize_range(0..self.len()) + } + } + + impl<'a> EntriesMut<'a, $I> for [$T] { + type IterMut = $IterMut<'a>; + fn entries_mut(&'a mut self) -> Self::IterMut { + $IterMut { + iter: self.iter_mut().enumerate(), + } + } + } + + $( + impl TryIndex<$I> for [$T] { + type Error = $Error; + + fn try_index(&self, idx: $I) -> Result<&Self::Output, Self::Error> { + self.get(idx.get()).ok_or($Error { idx }) + } + } + + impl TryIndexMut<$I> for [$T] { + fn try_index_mut(&mut self, idx: $I) -> Result<&mut Self::Output, Self::Error> { + self.get_mut(idx.get()).ok_or($Error { idx }) + } + } + )? + }; +} + +impl_index! { + #[error = SSAValIdxOutOfRange, iter = SSAValEntriesIter, iter_mut = SSAValEntriesIterMut] + impl Index for Vec {} +} + +impl_index! { + #[error = BlockParamIdxOutOfRange, iter = BlockParamEntriesIter, iter_mut = BlockParamEntriesIterMut] + impl Index for Vec {} +} + +impl_index! { + #[error = OperandIdxOutOfRange, iter = OperandEntriesIter, iter_mut = OperandEntriesIterMut] + impl Index for Vec {} +} + +impl_index! { + #[error = InstIdxOutOfRange, iter = InstEntriesIter, iter_mut = InstEntriesIterMut] + impl Index for Vec {} +} + +impl_index! { + #[error = BlockIdxOutOfRange, iter = BlockEntriesIter, iter_mut = BlockEntriesIterMut] + impl Index for Vec {} +} + +impl_index! { + #[iter = LiveRangeEntriesIter, iter_mut = LiveRangeEntriesIterMut] + impl Index for Vec {} +} impl fmt::Display for SSAValIdx { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -188,6 +477,12 @@ impl fmt::Display for OperandIdx { } } +impl fmt::Display for LiveRangeIdx { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "live_range{}", self.get()) + } +} + impl BlockIdx { pub const ENTRY_BLOCK: BlockIdx = BlockIdx::new(0); } diff --git a/register_allocator/src/interned.rs b/register_allocator/src/interned.rs index 2eb61d7..ce58c0b 100644 --- a/register_allocator/src/interned.rs +++ b/register_allocator/src/interned.rs @@ -215,6 +215,12 @@ pub struct GlobalState { interners: Interners, } +impl fmt::Debug for GlobalState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("GlobalState").finish_non_exhaustive() + } +} + scoped_tls::scoped_thread_local!(static GLOBAL_STATE: Rc); impl GlobalState { diff --git a/register_allocator/src/lib.rs b/register_allocator/src/lib.rs index c359562..6131f8b 100644 --- a/register_allocator/src/lib.rs +++ b/register_allocator/src/lib.rs @@ -5,5 +5,7 @@ pub mod function; pub mod fuzzing; pub mod index; pub mod interned; +pub mod live_range; pub mod loc; pub mod loc_set; +pub mod state; diff --git a/register_allocator/src/live_range.rs b/register_allocator/src/live_range.rs new file mode 100644 index 0000000..239120f --- /dev/null +++ b/register_allocator/src/live_range.rs @@ -0,0 +1,179 @@ +use crate::{ + function::ProgPoint, + index::{LiveRangeIdx, SSAValIdx}, + loc::Loc, +}; +use std::{cmp::Ordering, collections::BTreeSet, iter::FusedIterator, ops::Range}; + +#[derive(Copy, Clone, Debug)] +/// a Range except that any overlapping ranges compare equal +pub struct ProgRange { + pub start: ProgPoint, + pub end: ProgPoint, +} + +impl ProgRange { + pub const fn is_empty(self) -> bool { + self.len() == 0 + } + pub const fn len(self) -> usize { + self.end.as_usize().saturating_sub(self.start.as_usize()) + } + pub fn overlaps(self, other: Self) -> bool { + self.start < other.end && other.start < self.end + } + pub const fn as_usize_range(self) -> Range { + self.start.as_usize()..self.end.as_usize() + } + pub const fn from_usize_range(value: Range) -> Self { + Self { + start: ProgPoint::from_usize(value.start), + end: ProgPoint::from_usize(value.end), + } + } + pub fn with_self_as_usize_range) -> R>(&mut self, f: F) -> R { + let mut range = self.as_usize_range(); + let retval = f(&mut range); + *self = Self::from_usize_range(range); + retval + } +} + +impl Eq for ProgRange {} + +impl PartialEq for ProgRange { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl PartialOrd for ProgRange { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for ProgRange { + fn cmp(&self, other: &Self) -> Ordering { + if self.overlaps(*other) { + Ordering::Equal + } else { + (self.start, self.end).cmp(&(other.start, other.end)) + } + } +} + +impl IntoIterator for ProgRange { + type Item = ProgPoint; + type IntoIter = ProgRangeIter; + + fn into_iter(self) -> Self::IntoIter { + ProgRangeIter { range: self } + } +} + +#[derive(Clone, Debug)] +pub struct ProgRangeIter { + pub range: ProgRange, +} + +impl Iterator for ProgRangeIter { + type Item = ProgPoint; + + fn next(&mut self) -> Option { + self.range + .with_self_as_usize_range(|range| range.next()) + .map(ProgPoint::from_usize) + } + + fn size_hint(&self) -> (usize, Option) { + (self.len(), Some(self.len())) + } + + fn count(self) -> usize { + self.range.as_usize_range().count() + } + + fn last(self) -> Option { + self.range + .as_usize_range() + .last() + .map(ProgPoint::from_usize) + } + + fn nth(&mut self, n: usize) -> Option { + self.range + .with_self_as_usize_range(|range| range.nth(n)) + .map(ProgPoint::from_usize) + } + + fn fold(mut self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.range.with_self_as_usize_range(|range| { + range.fold(init, |a, b| f(a, ProgPoint::from_usize(b))) + }) + } +} + +impl DoubleEndedIterator for ProgRangeIter { + fn next_back(&mut self) -> Option { + self.range + .with_self_as_usize_range(|range| range.next_back()) + .map(ProgPoint::from_usize) + } + + fn nth_back(&mut self, n: usize) -> Option { + self.range + .with_self_as_usize_range(|range| range.nth_back(n)) + .map(ProgPoint::from_usize) + } + + fn rfold(mut self, init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + self.range.with_self_as_usize_range(|range| { + range.rfold(init, |a, b| f(a, ProgPoint::from_usize(b))) + }) + } +} + +impl FusedIterator for ProgRangeIter {} + +impl ExactSizeIterator for ProgRangeIter { + fn len(&self) -> usize { + self.range.as_usize_range().len() + } +} + +/// ensure Hash isn't implemented for ProgRange, +/// since afaict there isn't a way to implement it consistently with Ord +fn _check_prog_range_is_not_hashable() { + struct Hashable; + trait ProgRangeHashCheck: Sized { + fn _f(self) -> Hashable { + Hashable + } + } + struct S([T; 0]); + impl S { + fn _f(self) -> Hashable { + Hashable + } + } + impl ProgRangeHashCheck for S {} + let _: Hashable = S::([])._f(); +} + +#[derive(Clone, Debug)] +pub struct LiveRange { + pub range: ProgRange, + pub ssa_val: SSAValIdx, +} + +pub struct LiveRangeBundle { + pub live_ranges: BTreeSet, + pub allocation: Option, +} diff --git a/register_allocator/src/state.rs b/register_allocator/src/state.rs new file mode 100644 index 0000000..103233a --- /dev/null +++ b/register_allocator/src/state.rs @@ -0,0 +1,8 @@ +use crate::{function::Function, interned::GlobalState, live_range::LiveRange}; + +#[derive(Debug)] +pub struct AllocatorState<'a> { + global_state: &'a GlobalState, + func: &'a Function, + live_ranges: Vec, +}