use alloc::vec::Vec;
use core::fmt;
use core::str::SplitWhitespace;
use vize_l0::{String, cstr};
use crate::dump::value::DumpValue;
use crate::dump::{Dump, Error as DumpError};
use crate::pass::{Fusability, PassKind, Pipeline};
#[derive(Debug, Clone, Default, PartialEq, Eq, Dump)]
#[dump(name = "fusion-plan-folio")]
pub struct Page {
pub stage: String,
pub walks: u32,
pub passes: Vec<Pass>,
}
impl Page {
#[must_use]
pub fn of(pipeline: &Pipeline) -> Self {
let walks = pipeline.group_count();
let mut passes = Vec::with_capacity(pipeline.passes.len());
for walk in 0..walks {
let Some(group) = pipeline.group(walk) else {
continue;
};
let members = pipeline.passes.get(group.start..group.end());
for desc in members.unwrap_or_default() {
passes.push(Pass {
walk: walk_number(walk),
pass: String::from(desc.name),
kind: desc.kind,
fusability: desc.fusability,
});
}
}
Self {
stage: String::from(pipeline.stage),
walks: walk_number(walks),
passes,
}
}
}
fn walk_number(index: usize) -> u32 {
u32::try_from(index).unwrap_or(u32::MAX)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pass {
pub walk: u32,
pub pass: String,
pub kind: PassKind,
pub fusability: Fusability,
}
impl DumpValue for Pass {
fn print_value<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
write!(
w,
"walk={} pass={} kind={} fusability={}",
self.walk,
self.pass,
self.kind.as_str(),
self.fusability.as_str()
)
}
fn parse_value(text: &str, line: usize) -> Result<Self, DumpError> {
let mut fields = text.split_whitespace();
let walk_text = field(&mut fields, "walk", line)?;
let walk = walk_text
.parse()
.map_err(|_| DumpError::new(line, cstr!("invalid `walk` integer `{walk_text}`")))?;
let pass = field(&mut fields, "pass", line)?;
if pass.is_empty() {
return Err(DumpError::new(line, String::from("empty pass name")));
}
let kind_text = field(&mut fields, "kind", line)?;
let kind = PassKind::from_str(kind_text)
.ok_or_else(|| DumpError::new(line, cstr!("unknown pass kind `{kind_text}`")))?;
let fusability_text = field(&mut fields, "fusability", line)?;
let fusability = Fusability::from_str(fusability_text)
.ok_or_else(|| DumpError::new(line, cstr!("unknown fusability `{fusability_text}`")))?;
if let Some(extra) = fields.next() {
return Err(DumpError::new(line, cstr!("unexpected field `{extra}`")));
}
Ok(Self {
walk,
pass: String::from(pass),
kind,
fusability,
})
}
}
fn field<'a>(
fields: &mut SplitWhitespace<'a>,
name: &str,
line: usize,
) -> Result<&'a str, DumpError> {
let Some(raw) = fields.next() else {
return Err(DumpError::new(line, cstr!("missing `{name}` field")));
};
raw.strip_prefix(name)
.and_then(|rest| rest.strip_prefix('='))
.ok_or_else(|| DumpError::new(line, cstr!("expected `{name}=...`, got `{raw}`")))
}