forked from libre-chip/cpu
		
	WIP adding register allocator
This commit is contained in:
		
							parent
							
								
									d0aa86e335
								
							
						
					
					
						commit
						cb5855589f
					
				
					 9 changed files with 570 additions and 100 deletions
				
			
		
							
								
								
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -279,7 +279,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" | |||
| [[package]] | ||||
| name = "fayalite" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| dependencies = [ | ||||
|  "bitvec", | ||||
|  "blake3", | ||||
|  | @ -300,7 +300,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "fayalite-proc-macros" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| dependencies = [ | ||||
|  "fayalite-proc-macros-impl", | ||||
| ] | ||||
|  | @ -308,7 +308,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "fayalite-proc-macros-impl" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| dependencies = [ | ||||
|  "base16ct", | ||||
|  "num-bigint", | ||||
|  | @ -323,7 +323,7 @@ dependencies = [ | |||
| [[package]] | ||||
| name = "fayalite-visit-gen" | ||||
| version = "0.2.0" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#d0229fbcfb11666b5abf7ae487ffe335866f9326" | ||||
| source = "git+https://git.libre-chip.org/libre-chip/fayalite.git?branch=master#3939ce2360138c75c53972368069c8f882111571" | ||||
| dependencies = [ | ||||
|  "indexmap", | ||||
|  "prettyplease", | ||||
|  |  | |||
|  | @ -1,18 +1,21 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::instruction::{PRegNum, UnitKind, UnitNum}; | ||||
| use crate::{ | ||||
|     instruction::{PRegNum, UnitNum, CONST_ZERO_UNIT_NUM}, | ||||
|     unit::UnitKind, | ||||
| }; | ||||
| use fayalite::prelude::*; | ||||
| 
 | ||||
| #[derive(Clone, Eq, PartialEq, Hash, Debug)] | ||||
| #[non_exhaustive] | ||||
| pub struct CpuConfig { | ||||
|     pub units: Vec<UnitKind>, | ||||
|     pub unit_kinds: Vec<UnitKind>, | ||||
|     pub out_reg_num_width: usize, | ||||
| } | ||||
| 
 | ||||
| impl CpuConfig { | ||||
|     pub fn unit_num_width(&self) -> usize { | ||||
|         UInt::range(0..self.units.len()).width() | ||||
|         UInt::range((CONST_ZERO_UNIT_NUM + 1)..self.unit_kinds.len()).width() | ||||
|     } | ||||
|     pub fn unit_num(&self) -> UnitNum<DynSize> { | ||||
|         UnitNum[self.unit_num_width()] | ||||
|  |  | |||
|  | @ -1,102 +1,11 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::config::CpuConfig; | ||||
| use crate::unit::UnitMOp; | ||||
| use fayalite::prelude::*; | ||||
| use std::marker::PhantomData; | ||||
| 
 | ||||
| pub mod power_isa; | ||||
| 
 | ||||
| macro_rules! all_units { | ||||
|     ( | ||||
|         #[hdl_unit_kind = $HdlUnitKind:ident] | ||||
|         #[unit_kind = $UnitKind:ident] | ||||
|         #[hdl] | ||||
|         $(#[$enum_meta:meta])* | ||||
|         $vis:vis enum $UnitMOpEnum:ident<$RegWidth:ident: Size> { | ||||
|             $( | ||||
|                 $(#[$variant_meta:meta])* | ||||
|                 $Unit:ident($Op:ty), | ||||
|             )* | ||||
|         } | ||||
|     ) => { | ||||
|         $(#[$enum_meta])* | ||||
|         #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] | ||||
|         $vis enum $UnitKind { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Unit, | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         impl ToExpr for $UnitKind { | ||||
|             type Type = $HdlUnitKind; | ||||
| 
 | ||||
|             fn to_expr(&self) -> Expr<Self::Type> { | ||||
|                 match self { | ||||
|                     $($UnitKind::$Unit => $HdlUnitKind.$Unit(),)* | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[hdl] | ||||
|         $(#[$enum_meta])* | ||||
|         $vis enum $HdlUnitKind { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Unit, | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         #[hdl] | ||||
|         $(#[$enum_meta])* | ||||
|         $vis enum $UnitMOpEnum<$RegWidth: Size> { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Unit($Op), | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         impl<$RegWidth: Size> $UnitMOpEnum<$RegWidth> { | ||||
|             #[hdl] | ||||
|             $vis fn kind(expr: impl ToExpr<Type = Self>) -> Expr<$HdlUnitKind> { | ||||
|                 #[hdl] | ||||
|                 let unit_kind = wire(); | ||||
|                 #[hdl] | ||||
|                 match expr { | ||||
|                     $($UnitMOpEnum::<$RegWidth>::$Unit(_) => connect(unit_kind, $HdlUnitKind.$Unit()),)* | ||||
|                 } | ||||
|                 unit_kind | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl CpuConfig { | ||||
|             #[hdl] | ||||
|             pub fn available_units_for_kind(&self, unit_kind: impl ToExpr<Type = $HdlUnitKind>) -> Expr<Array<Bool>> { | ||||
|                 #[hdl] | ||||
|                 let available_units_for_kind = wire(Array[Bool][self.units.len()]); | ||||
|                 #[hdl] | ||||
|                 match unit_kind { | ||||
|                     $($HdlUnitKind::$Unit => for (index, &unit) in self.units.iter().enumerate() { | ||||
|                         connect(available_units_for_kind[index], unit == $UnitKind::$Unit); | ||||
|                     })* | ||||
|                 } | ||||
|                 available_units_for_kind | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| all_units! { | ||||
|     #[hdl_unit_kind = HdlUnitKind] | ||||
|     #[unit_kind = UnitKind] | ||||
|     #[hdl] | ||||
|     pub enum UnitMOp<RegWidth: Size> { | ||||
|         AluBranch(AluBranchMOp<RegWidth>), | ||||
|         L2RegisterFile(L2RegisterFileMOp<RegWidth>), | ||||
|         LoadStore(LoadStoreMOp<RegWidth>), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub enum OutputIntegerMode { | ||||
|     Full64, | ||||
|  | @ -239,11 +148,14 @@ pub enum LoadStoreMOp<RegWidth: Size> { | |||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| /// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind
 | ||||
| /// there may be more than one unit of a given kind, so UnitNum is not the same as UnitKind.
 | ||||
| /// zero is used for built-in constants, such as the zero register
 | ||||
| pub struct UnitNum<Width: Size> { | ||||
|     pub value: UIntType<Width>, | ||||
| } | ||||
| 
 | ||||
| pub const CONST_ZERO_UNIT_NUM: usize = 0; | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct UnitOutRegNum<Width: Size> { | ||||
|     pub value: UIntType<Width>, | ||||
|  |  | |||
|  | @ -2,5 +2,7 @@ | |||
| // See Notices.txt for copyright information
 | ||||
| pub mod config; | ||||
| pub mod instruction; | ||||
| pub mod reg_alloc; | ||||
| pub mod register; | ||||
| pub mod unit; | ||||
| pub mod util; | ||||
|  |  | |||
							
								
								
									
										11
									
								
								crates/cpu/src/reg_alloc.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								crates/cpu/src/reg_alloc.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::config::CpuConfig; | ||||
| use fayalite::prelude::*; | ||||
| 
 | ||||
| pub mod unit_free_regs_tracker; | ||||
| 
 | ||||
| #[hdl_module] | ||||
| pub fn reg_alloc(config: CpuConfig) { | ||||
|     todo!() | ||||
| } | ||||
							
								
								
									
										110
									
								
								crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								crates/cpu/src/reg_alloc/unit_free_regs_tracker.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| use crate::util::tree_reduce::tree_reduce; | ||||
| use fayalite::{prelude::*, util::ready_valid::ReadyValid}; | ||||
| use std::{num::NonZeroUsize, ops::Range}; | ||||
| 
 | ||||
| #[hdl_module] | ||||
| pub fn unit_free_regs_tracker( | ||||
|     free_at_once: NonZeroUsize, | ||||
|     alloc_at_once: NonZeroUsize, | ||||
|     reg_num_width: usize, | ||||
| ) { | ||||
|     #[hdl] | ||||
|     let cd: ClockDomain = m.input(); | ||||
|     #[hdl] | ||||
|     let free_in: Array<ReadyValid<UInt>> = | ||||
|         m.input(Array[ReadyValid[UInt[reg_num_width]]][free_at_once.get()]); | ||||
|     #[hdl] | ||||
|     let alloc_out: Array<ReadyValid<UInt>> = | ||||
|         m.input(Array[ReadyValid[UInt[reg_num_width]]][alloc_at_once.get()]); | ||||
|     assert!( | ||||
|         reg_num_width <= isize::MAX.ilog2() as usize, | ||||
|         "too many registers" | ||||
|     ); | ||||
|     let reg_count = 1usize << reg_num_width; | ||||
|     #[hdl] | ||||
|     let allocated_reg = reg_builder().clock_domain(cd).reset(vec![false; reg_count]); | ||||
|     for free_index in 0..free_at_once.get() { | ||||
|         connect(free_in[free_index].ready, true); | ||||
|         #[hdl] | ||||
|         if let HdlSome(free_num) = ReadyValid::firing_data(free_in[free_index]) { | ||||
|             connect(allocated_reg[free_num], false); | ||||
|         } | ||||
|     } | ||||
|     struct Summary { | ||||
|         range: Range<usize>, | ||||
|         count: Expr<UInt>, | ||||
|         count_overflowed: Expr<Bool>, | ||||
|         alloc_nums: Expr<Array<UInt>>, | ||||
|     } | ||||
|     let count_ty = UInt::range_inclusive(0..=alloc_at_once.get()); | ||||
|     let Some(Summary { | ||||
|         range: _, | ||||
|         count, | ||||
|         count_overflowed, | ||||
|         alloc_nums, | ||||
|     }) = tree_reduce( | ||||
|         (0..reg_count).map(|index| Summary { | ||||
|             range: index..index + 1, | ||||
|             count: allocated_reg[index] | ||||
|                 .cast_to_static::<UInt<1>>() | ||||
|                 .cast_to(count_ty), | ||||
|             count_overflowed: false.to_expr(), | ||||
|             alloc_nums: repeat(UInt[0].zero(), alloc_at_once.get()), | ||||
|         }), | ||||
|         |l, r| { | ||||
|             let range = l.range.start..r.range.end; | ||||
|             #[hdl] | ||||
|             let reduced_count = wire(count_ty); | ||||
|             connect_any(reduced_count, l.count + r.count); | ||||
|             #[hdl] | ||||
|             let reduced_count_overflowed = wire(); | ||||
|             connect( | ||||
|                 reduced_count_overflowed, | ||||
|                 (l.count + r.count).cmp_ne(reduced_count) | l.count_overflowed | r.count_overflowed, | ||||
|             ); | ||||
|             #[hdl] | ||||
|             let reduced_alloc_nums = wire( | ||||
|                 Array[UInt[Expr::ty(l.alloc_nums).element().width() + 1]][alloc_at_once.get()], | ||||
|             ); | ||||
|             for alloc_index in 0..alloc_at_once.get() { | ||||
|                 #[hdl] | ||||
|                 if l.count_overflowed | l.count.cmp_ge(alloc_at_once) { | ||||
|                     connect(reduced_alloc_nums[alloc_index], l.alloc_nums[alloc_index]); | ||||
|                 } else { | ||||
|                     connect(reduced_alloc_nums[alloc_index], r.alloc_nums[alloc_index]); | ||||
|                 } | ||||
|             } | ||||
|             Summary { | ||||
|                 range, | ||||
|                 count: reduced_count, | ||||
|                 count_overflowed: reduced_count_overflowed, | ||||
|                 alloc_nums: reduced_alloc_nums, | ||||
|             } | ||||
|         }, | ||||
|     ) | ||||
|     else { | ||||
|         unreachable!("reg_count is known to be non-zero"); | ||||
|     }; | ||||
|     for alloc_index in 0..alloc_at_once.get() { | ||||
|         #[hdl] | ||||
|         if let HdlSome(alloc_num) = ReadyValid::firing_data(alloc_out[alloc_index]) { | ||||
|             connect(allocated_reg[alloc_num], true); | ||||
|         } | ||||
|         #[hdl] | ||||
|         if count_overflowed | count.cmp_ge(alloc_index) { | ||||
|             connect( | ||||
|                 alloc_out[alloc_index].data, | ||||
|                 HdlSome(alloc_nums[alloc_index]), | ||||
|             ); | ||||
|         } else { | ||||
|             connect( | ||||
|                 alloc_out[alloc_index].data, | ||||
|                 HdlOption[UInt[reg_num_width]].HdlNone(), | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // TODO: add formal proof of unit_free_regs_tracker
 | ||||
							
								
								
									
										277
									
								
								crates/cpu/src/unit.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								crates/cpu/src/unit.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,277 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| use crate::{ | ||||
|     config::CpuConfig, | ||||
|     instruction::{AluBranchMOp, L2RegisterFileMOp, LoadStoreMOp, PRegNum}, | ||||
|     register::PRegValue, | ||||
| }; | ||||
| use fayalite::{ | ||||
|     bundle::{Bundle, BundleType}, | ||||
|     intern::{Intern, Interned}, | ||||
|     prelude::*, | ||||
|     util::ready_valid::ReadyValid, | ||||
| }; | ||||
| 
 | ||||
| macro_rules! all_units { | ||||
|     ( | ||||
|         #[hdl_unit_kind = $HdlUnitKind:ident] | ||||
|         #[unit_kind = $UnitKind:ident] | ||||
|         #[hdl] | ||||
|         $(#[$enum_meta:meta])* | ||||
|         $vis:vis enum $UnitMOpEnum:ident<$RegWidth:ident: Size> { | ||||
|             $( | ||||
|                 $(#[$variant_meta:meta])* | ||||
|                 $Unit:ident($Op:ty), | ||||
|             )* | ||||
|         } | ||||
|     ) => { | ||||
|         $(#[$enum_meta])* | ||||
|         #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] | ||||
|         $vis enum $UnitKind { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Unit, | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         impl ToExpr for $UnitKind { | ||||
|             type Type = $HdlUnitKind; | ||||
| 
 | ||||
|             fn to_expr(&self) -> Expr<Self::Type> { | ||||
|                 match self { | ||||
|                     $($UnitKind::$Unit => $HdlUnitKind.$Unit(),)* | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[hdl] | ||||
|         $(#[$enum_meta])* | ||||
|         $vis enum $HdlUnitKind { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Unit, | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         #[hdl] | ||||
|         $(#[$enum_meta])* | ||||
|         $vis enum $UnitMOpEnum<$RegWidth: Size> { | ||||
|             $( | ||||
|                 $(#[$variant_meta])* | ||||
|                 $Unit($Op), | ||||
|             )* | ||||
|         } | ||||
| 
 | ||||
|         impl<$RegWidth: Size> $UnitMOpEnum<$RegWidth> { | ||||
|             #[hdl] | ||||
|             $vis fn kind(expr: impl ToExpr<Type = Self>) -> Expr<$HdlUnitKind> { | ||||
|                 #[hdl] | ||||
|                 let unit_kind = wire(); | ||||
|                 #[hdl] | ||||
|                 match expr { | ||||
|                     $($UnitMOpEnum::<$RegWidth>::$Unit(_) => connect(unit_kind, $HdlUnitKind.$Unit()),)* | ||||
|                 } | ||||
|                 unit_kind | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         impl CpuConfig { | ||||
|             #[hdl] | ||||
|             pub fn available_units_for_kind(&self, unit_kind: impl ToExpr<Type = $HdlUnitKind>) -> Expr<Array<Bool>> { | ||||
|                 #[hdl] | ||||
|                 let available_units_for_kind = wire(Array[Bool][self.unit_kinds.len()]); | ||||
|                 #[hdl] | ||||
|                 match unit_kind { | ||||
|                     $($HdlUnitKind::$Unit => for (index, &unit_kind) in self.unit_kinds.iter().enumerate() { | ||||
|                         connect(available_units_for_kind[index], unit_kind == $UnitKind::$Unit); | ||||
|                     })* | ||||
|                 } | ||||
|                 available_units_for_kind | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| all_units! { | ||||
|     #[hdl_unit_kind = HdlUnitKind] | ||||
|     #[unit_kind = UnitKind] | ||||
|     #[hdl] | ||||
|     pub enum UnitMOp<RegWidth: Size> { | ||||
|         AluBranch(AluBranchMOp<RegWidth>), | ||||
|         L2RegisterFile(L2RegisterFileMOp<RegWidth>), | ||||
|         LoadStore(LoadStoreMOp<RegWidth>), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct UnitResultCompleted<ExtraOut> { | ||||
|     pub value: PRegValue, | ||||
|     pub extra_out: ExtraOut, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct TrapData { | ||||
|     // TODO
 | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub enum UnitResult<ExtraOut> { | ||||
|     Completed(UnitResultCompleted<ExtraOut>), | ||||
|     Trap(TrapData), | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct UnitOutput<UnitNumWidth: Size, OutRegNumWidth: Size, ExtraOut> { | ||||
|     pub which: PRegNum<UnitNumWidth, OutRegNumWidth>, | ||||
|     pub result: UnitResult<ExtraOut>, | ||||
| } | ||||
| 
 | ||||
| #[hdl] | ||||
| pub struct UnitCancelInput<UnitNumWidth: Size, OutRegNumWidth: Size> { | ||||
|     pub which: PRegNum<UnitNumWidth, OutRegNumWidth>, | ||||
| } | ||||
| 
 | ||||
| pub trait UnitTrait: | ||||
|     'static + Send + Sync + std::fmt::Debug + fayalite::intern::SupportsPtrEqWithTypeId | ||||
| { | ||||
|     type Type: BundleType; | ||||
|     type ExtraOut: Type; | ||||
|     type MOp: Type; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type; | ||||
|     fn extra_out_ty(&self) -> Self::ExtraOut; | ||||
|     fn mop_ty(&self) -> Self::MOp; | ||||
| 
 | ||||
|     fn unit_kind(&self) -> UnitKind; | ||||
| 
 | ||||
|     fn make_module(&self) -> Interned<Module<Self::Type>>; | ||||
| 
 | ||||
|     // TODO: add other inputs
 | ||||
|     fn cancel_input( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitCancelInput<DynSize, DynSize>>>; | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, DynSize, Self::ExtraOut>>>; | ||||
| 
 | ||||
|     fn to_dyn(&self) -> DynUnit; | ||||
| } | ||||
| 
 | ||||
| type DynUnitTrait = dyn UnitTrait<Type = Bundle, ExtraOut = CanonicalType, MOp = CanonicalType>; | ||||
| 
 | ||||
| impl fayalite::intern::InternedCompare for DynUnitTrait { | ||||
|     type InternedCompareKey = fayalite::intern::PtrEqWithTypeId; | ||||
|     fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey { | ||||
|         Self::get_ptr_eq_with_type_id(this) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct DynUnit { | ||||
|     ty: Bundle, | ||||
|     extra_out_ty: CanonicalType, | ||||
|     mop_ty: CanonicalType, | ||||
|     unit_kind: UnitKind, | ||||
|     unit: Interned<DynUnitTrait>, | ||||
| } | ||||
| 
 | ||||
| impl UnitTrait for DynUnit { | ||||
|     type Type = Bundle; | ||||
|     type ExtraOut = CanonicalType; | ||||
|     type MOp = CanonicalType; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         self.ty | ||||
|     } | ||||
| 
 | ||||
|     fn extra_out_ty(&self) -> Self::ExtraOut { | ||||
|         self.extra_out_ty | ||||
|     } | ||||
| 
 | ||||
|     fn mop_ty(&self) -> Self::MOp { | ||||
|         self.mop_ty | ||||
|     } | ||||
| 
 | ||||
|     fn unit_kind(&self) -> UnitKind { | ||||
|         self.unit_kind | ||||
|     } | ||||
| 
 | ||||
|     fn make_module(&self) -> Interned<Module<Self::Type>> { | ||||
|         self.unit.make_module() | ||||
|     } | ||||
| 
 | ||||
|     fn cancel_input( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitCancelInput<DynSize, DynSize>>> { | ||||
|         self.unit.cancel_input(this) | ||||
|     } | ||||
| 
 | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, DynSize, Self::ExtraOut>>> { | ||||
|         self.unit.output(this) | ||||
|     } | ||||
| 
 | ||||
|     fn to_dyn(&self) -> DynUnit { | ||||
|         *self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub struct DynUnitWrapper<T>(pub T); | ||||
| 
 | ||||
| impl<T: UnitTrait + Clone + std::hash::Hash + Eq> UnitTrait for DynUnitWrapper<T> { | ||||
|     type Type = Bundle; | ||||
|     type ExtraOut = CanonicalType; | ||||
|     type MOp = CanonicalType; | ||||
| 
 | ||||
|     fn ty(&self) -> Self::Type { | ||||
|         Bundle::from_canonical(self.0.ty().canonical()) | ||||
|     } | ||||
| 
 | ||||
|     fn extra_out_ty(&self) -> Self::ExtraOut { | ||||
|         self.0.extra_out_ty().canonical() | ||||
|     } | ||||
| 
 | ||||
|     fn mop_ty(&self) -> Self::MOp { | ||||
|         self.0.mop_ty().canonical() | ||||
|     } | ||||
| 
 | ||||
|     fn unit_kind(&self) -> UnitKind { | ||||
|         self.0.unit_kind() | ||||
|     } | ||||
| 
 | ||||
|     fn make_module(&self) -> Interned<Module<Self::Type>> { | ||||
|         self.0.make_module().canonical().intern_sized() | ||||
|     } | ||||
| 
 | ||||
|     fn cancel_input( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitCancelInput<DynSize, DynSize>>> { | ||||
|         self.0.cancel_input(Expr::from_bundle(this)) | ||||
|     } | ||||
| 
 | ||||
|     fn output( | ||||
|         &self, | ||||
|         this: Expr<Self::Type>, | ||||
|     ) -> Expr<ReadyValid<UnitOutput<DynSize, DynSize, Self::ExtraOut>>> { | ||||
|         Expr::from_bundle(Expr::as_bundle(self.0.output(Expr::from_bundle(this)))) | ||||
|     } | ||||
| 
 | ||||
|     fn to_dyn(&self) -> DynUnit { | ||||
|         let unit = self.intern(); | ||||
|         DynUnit { | ||||
|             ty: unit.ty(), | ||||
|             extra_out_ty: unit.extra_out_ty(), | ||||
|             mop_ty: unit.mop_ty(), | ||||
|             unit_kind: unit.unit_kind(), | ||||
|             unit: Interned::cast_unchecked(unit, |v: &Self| -> &DynUnitTrait { v }), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,5 +1,8 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| 
 | ||||
| pub mod tree_reduce; | ||||
| 
 | ||||
| pub(crate) const fn range_u32_len(range: &std::ops::Range<u32>) -> usize { | ||||
|     let retval = range.end.saturating_sub(range.start); | ||||
|     assert!(retval as usize as u32 != retval, "len overflowed"); | ||||
|  |  | |||
							
								
								
									
										152
									
								
								crates/cpu/src/util/tree_reduce.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								crates/cpu/src/util/tree_reduce.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,152 @@ | |||
| // SPDX-License-Identifier: LGPL-3.0-or-later
 | ||||
| // See Notices.txt for copyright information
 | ||||
| #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | ||||
| pub enum TreeReduceOp { | ||||
|     Input, | ||||
|     Reduce, | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone, Debug)] | ||||
| struct Entry { | ||||
|     start: usize, | ||||
|     depth: u32, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct TreeReduceOps { | ||||
|     len: usize, | ||||
|     stack: Vec<Entry>, | ||||
| } | ||||
| 
 | ||||
| impl TreeReduceOps { | ||||
|     pub fn new(len: usize) -> Self { | ||||
|         TreeReduceOps { | ||||
|             len, | ||||
|             stack: Vec::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Iterator for TreeReduceOps { | ||||
|     type Item = TreeReduceOp; | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         match *self.stack { | ||||
|             [] if self.len != 0 => { | ||||
|                 self.stack.push(Entry { start: 0, depth: 0 }); | ||||
|                 Some(TreeReduceOp::Input) | ||||
|             } | ||||
|             [.., ref mut second_last, last] if second_last.depth == last.depth => { | ||||
|                 second_last.depth += 1; | ||||
|                 self.stack.pop(); | ||||
|                 Some(TreeReduceOp::Reduce) | ||||
|             } | ||||
|             [.., last] if self.len - last.start > 1 << last.depth => { | ||||
|                 let start = last.start + (1 << last.depth); | ||||
|                 self.stack.push(Entry { start, depth: 0 }); | ||||
|                 Some(TreeReduceOp::Input) | ||||
|             } | ||||
|             [.., ref mut second_last, _] => { | ||||
|                 second_last.depth += 1; | ||||
|                 self.stack.pop(); | ||||
|                 Some(TreeReduceOp::Reduce) | ||||
|             } | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[track_caller] | ||||
| pub fn tree_reduce_with_state<S, I, R>( | ||||
|     iter: impl IntoIterator<IntoIter: ExactSizeIterator, Item = I>, | ||||
|     state: &mut S, | ||||
|     mut input: impl FnMut(&mut S, I) -> R, | ||||
|     mut reduce: impl FnMut(&mut S, R, R) -> R, | ||||
| ) -> Option<R> { | ||||
|     let mut stack = Vec::new(); | ||||
|     let mut iter = iter.into_iter(); | ||||
|     for op in TreeReduceOps::new(iter.len()) { | ||||
|         match op { | ||||
|             TreeReduceOp::Input => stack.push(input( | ||||
|                 state, | ||||
|                 iter.next().expect("inconsistent iterator len() and next()"), | ||||
|             )), | ||||
|             TreeReduceOp::Reduce => { | ||||
|                 let Some(r) = stack.pop() else { | ||||
|                     unreachable!(); | ||||
|                 }; | ||||
|                 let Some(l) = stack.pop() else { | ||||
|                     unreachable!(); | ||||
|                 }; | ||||
|                 stack.push(reduce(state, l, r)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     stack.pop() | ||||
| } | ||||
| 
 | ||||
| pub fn tree_reduce<T>( | ||||
|     iter: impl IntoIterator<Item = T, IntoIter: ExactSizeIterator>, | ||||
|     mut reduce: impl FnMut(T, T) -> T, | ||||
| ) -> Option<T> { | ||||
|     tree_reduce_with_state(iter, &mut (), |_, v| v, move |_, l, r| reduce(l, r)) | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use std::ops::Range; | ||||
| 
 | ||||
|     fn recursive_tree_reduce(range: Range<usize>, ops: &mut Vec<TreeReduceOp>) { | ||||
|         if range.len() == 1 { | ||||
|             ops.push(TreeReduceOp::Input); | ||||
|             return; | ||||
|         } | ||||
|         if range.is_empty() { | ||||
|             return; | ||||
|         } | ||||
|         let pow2_len = range.len().next_power_of_two(); | ||||
|         let split = range.start + pow2_len / 2; | ||||
|         recursive_tree_reduce(range.start..split, ops); | ||||
|         recursive_tree_reduce(split..range.end, ops); | ||||
|         ops.push(TreeReduceOp::Reduce); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn test_tree_reduce() { | ||||
|         const EXPECTED: &'static [&'static [TreeReduceOp]] = { | ||||
|             use TreeReduceOp::{Input as I, Reduce as R}; | ||||
|             &[ | ||||
|                 &[], | ||||
|                 &[I], | ||||
|                 &[I, I, R], | ||||
|                 &[I, I, R, I, R], | ||||
|                 &[I, I, R, I, I, R, R], | ||||
|                 &[I, I, R, I, I, R, R, I, R], | ||||
|                 &[I, I, R, I, I, R, R, I, I, R, R], | ||||
|                 &[I, I, R, I, I, R, R, I, I, R, I, R, R], | ||||
|                 &[I, I, R, I, I, R, R, I, I, R, I, I, R, R, R], | ||||
|             ] | ||||
|         }; | ||||
|         for len in 0..64 { | ||||
|             let mut expected = vec![]; | ||||
|             recursive_tree_reduce(0..len, &mut expected); | ||||
|             if let Some(&expected2) = EXPECTED.get(len) { | ||||
|                 assert_eq!(*expected, *expected2, "len={len}"); | ||||
|             } | ||||
|             assert_eq!( | ||||
|                 TreeReduceOps::new(len).collect::<Vec<_>>(), | ||||
|                 expected, | ||||
|                 "len={len}" | ||||
|             ); | ||||
|             let seq: Vec<_> = (0..len).collect(); | ||||
|             assert_eq!( | ||||
|                 seq, | ||||
|                 tree_reduce(seq.iter().map(|&v| vec![v]), |mut l, r| { | ||||
|                     l.extend_from_slice(&r); | ||||
|                     l | ||||
|                 }) | ||||
|                 .unwrap_or_default() | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue