Install record layouts from the pass via set_layout

This commit is contained in:
Dennis Kobert
2026-08-15 11:20:01 +00:00
parent bfa48439c8
commit f9e30c4c77
10 changed files with 302 additions and 165 deletions

View File

@@ -278,6 +278,9 @@ pub trait Node<Input> {
crate::record::empty_layout()
}
/// Installs this node's resolved record layout; a no-op unless it produces records.
fn set_layout(&mut self, _layout: crate::record::RecordLayout) {}
fn eval_batch<'a>(&'a self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
where
Input: InjectIndex + Copy,

View File

@@ -243,6 +243,14 @@ pub fn empty_layout() -> &'static Layout {
}
/// Declarative record-io metadata for a node type, emitted by the macro into
/// A record node's output layout with the frame size and carrier copy plan derived from it.
#[derive(Clone, Debug, Default)]
pub struct RecordLayout {
pub layout: Layout,
pub frame_bytes: usize,
pub plan: Vec<(usize, usize, usize)>,
}
/// its registry entry so the compiler can fold each wire's layout without
/// running the node's constructor. [`fold`](LayoutMeta::fold) reproduces the
/// layout the constructor derives at wiring today; the compiler layout pass
@@ -287,8 +295,7 @@ pub enum ElementSpec {
}
impl LayoutMeta {
/// The meta of an elementwise carrier flip that retypes input 0's element,
/// preserving its depth and attributes: what an `Into`/`Convert` coercion derives.
/// Keeps input 0's layout but replaces its element.
pub fn retype(element: ElementWrite) -> Self {
Self {
sources: vec![0],
@@ -318,6 +325,23 @@ impl LayoutMeta {
};
base.with_writes(depth, element, &self.writes)
}
/// [`fold`](LayoutMeta::fold) with the frame size and carrier copy plan derived from it.
pub fn resolve(&self, inputs: &[Option<&Layout>]) -> RecordLayout {
let layout = self.fold(inputs);
let frame_bytes = layout.frame_bytes();
let plan = match self.sources.first() {
// A reducer collapses its carrier's levels, so it writes a fresh record rather than copying fields down.
Some(&source) if self.level_delta >= 0 => {
let from = inputs[source as usize].expect("layout resolve source input has no layout");
let carry_element = matches!(self.element, ElementSpec::Carried);
let removes: Vec<(&str, u8)> = self.removes.clone();
copy_plan(from, &layout, carry_element, &removes)
}
_ => Vec::new(),
};
RecordLayout { layout, frame_bytes, plan }
}
}
/// A view of one record: a pointer whose layout is proven at wiring.

View File

@@ -113,7 +113,6 @@ pub fn cache_key<C: CacheHash + ?Sized>(ctx: &C) -> u64 {
pub enum ConstructionError {
Arity { expected: usize, got: usize },
Type { expected: Box<Type>, found: Box<Type> },
MissingLayout,
}
pub struct SharedEdge<N: ?Sized> {
@@ -193,7 +192,8 @@ pub struct EdgeHandle {
node: Box<DynEdge>,
share: fn(&DynEdge) -> Box<DynEdge>,
serialize: fn(&DynEdge) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>,
layout: fn(&DynEdge) -> Option<&crate::record::Layout>,
layout: fn(&DynEdge) -> &crate::record::Layout,
set_layout: fn(&mut DynEdge, crate::record::RecordLayout),
ty: Type,
}
@@ -228,7 +228,12 @@ impl EdgeHandle {
node: Box::new(SharedEdge::new(node)),
share: |edge| Box::new(edge.downcast_ref::<SharedEdge<N>>().expect("share hook matches the stored edge type").share()),
serialize: |edge| Node::<ContextImpl>::serialize(edge.downcast_ref::<SharedEdge<N>>().expect("serialize hook matches the stored edge type")),
layout: |edge| Some(Node::<ContextImpl>::layout(edge.downcast_ref::<SharedEdge<N>>().expect("layout hook matches the stored edge type"))),
layout: |edge| Node::<ContextImpl>::layout(edge.downcast_ref::<SharedEdge<N>>().expect("layout hook matches the stored edge type")),
set_layout: |edge, layout| {
let shared = edge.downcast_mut::<SharedEdge<N>>().expect("set_layout hook matches the stored edge type");
let node = std::sync::Arc::get_mut(&mut shared.own).expect("layout is installed before the node is shared");
Node::<ContextImpl>::set_layout(node, layout);
},
ty,
}
}
@@ -243,6 +248,7 @@ impl EdgeHandle {
share: self.share,
serialize: self.serialize,
layout: self.layout,
set_layout: self.set_layout,
ty: self.ty.clone(),
}
}
@@ -251,10 +257,14 @@ impl EdgeHandle {
(self.serialize)(&*self.node)
}
pub fn layout(&self) -> Option<&crate::record::Layout> {
pub fn layout(&self) -> &crate::record::Layout {
(self.layout)(&*self.node)
}
pub fn set_layout(&mut self, layout: crate::record::RecordLayout) {
(self.set_layout)(&mut *self.node, layout);
}
pub fn downcast<T: 'static>(self) -> Result<SharedEdge<ErasedNode<T>>, ConstructionError> {
self.downcast_erased(edge_type::<T>())
}