This commit is contained in:
parent
d0aa86e335
commit
cb5855589f
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…
Reference in a new issue