Skip to content

Commit

Permalink
make stdlib::readback work in lazy mode
Browse files Browse the repository at this point in the history
  • Loading branch information
tjjfvi committed Apr 1, 2024
1 parent f92475b commit 3c414ce
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hvm-core"
version = "0.2.21"
version = "0.2.22"
edition = "2021"
description = "HVM-Core is a massively parallel Interaction Combinator evaluator."
license = "MIT"
Expand Down
7 changes: 7 additions & 0 deletions src/run/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,11 @@ impl<'a, M: Mode> Net<'a, M> {
self.link_port_port(port, Port::new_var(wire.addr()));
wire
}

/// Creates a wire pointing to a trg (this is a no-op if it is already a
/// wire); sometimes necessary to avoid deadlock.
#[inline(always)]
pub fn wire_to_trg(&mut self, trg: Trg) -> Wire {
if trg.is_wire() { trg.as_wire() } else { self.create_wire_to(trg.as_port()) }
}
}
113 changes: 46 additions & 67 deletions src/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,17 @@ pub struct LogDef<F>(Arc<Mutex<Host>>, F);
impl<F: Fn(Tree) + Clone + Send + Sync + 'static> AsHostedDef for LogDef<F> {
fn call<M: Mode>(def: &Def<Self>, net: &mut Net<M>, port: Port) {
let (arg, seq) = net.do_ctr(0, Trg::port(port));
let (wire, port) = net.create_wire();
if M::LAZY {
net.link_trg_port(arg, port);
net.link_trg_port(seq, Port::new_ref(unsafe { &*IDENTITY }));
net.normal_from(wire.clone());
def.data.1(def.data.0.lock().unwrap().readback_tree(&wire));
net.link_wire_port(wire, Port::ERA);
} else {
// SAFETY: the function inside `readback` won't live longer than
// the net, and thus won't live longer than the host, where the
// `&Def<Self>` points to
let def: &'static Def<Self> = unsafe { core::mem::transmute(def) };
let readback = readback(def.data.0.clone(), port, |net, tree| {
(def.data.1)(tree);
dispatch_dyn_net!(net => {
net.link_wire_port(wire, Port::ERA);
net.link_trg_port(seq, Port::new_ref(unsafe { &*IDENTITY }));
});
let seq = net.wire_to_trg(seq);
// SAFETY: the function inside `readback` won't live longer than
// the net, and thus won't live longer than the host, where the
// `&Def<Self>` points to
let def: &'static Def<Self> = unsafe { core::mem::transmute(def) };
readback(net, def.data.0.clone(), arg, |net, tree| {
(def.data.1)(tree);
dispatch_dyn_net!(net => {
net.link_wire_port(seq, Port::new_ref(unsafe { &*IDENTITY }));
});
net.link_trg_port(arg, readback);
}
});
}
}

Expand Down Expand Up @@ -172,7 +162,7 @@ impl<T: AsHostedDef> AsDef for HostedDef<T> {

use crate::ast::Tree;

#[derive(Clone)]
#[derive(Copy, Clone)]
pub struct UniqueTreePtr(*mut Tree);
unsafe impl Send for UniqueTreePtr {}
unsafe impl Sync for UniqueTreePtr {}
Expand All @@ -188,18 +178,16 @@ pub struct ReadbackDef<F: FnOnce(DynNetMut) + Send + Sync + 'static> {
host: Arc<Mutex<Host>>,
var_idx: Arc<AtomicUsize>,
tree: UniqueTreePtr,
out: Port,
}

impl<F: FnOnce(DynNetMut) + Send + Sync + 'static> ReadbackDef<F> {
fn maybe_finish<'a, 'b>(net: DynNetMut<'a, 'b>, root: Arc<F>) {
let Some(root) = Arc::into_inner(root) else { return };
(root)(net)
}
fn with(&self, tree: *mut Tree, out: Port) -> Port {
fn with(&self, tree: *mut Tree) -> Port {
Port::new_ref(Box::leak(BoxDef::new_boxed(LabSet::ALL, Self {
tree: UniqueTreePtr(tree),
out,
root: self.root.clone(),
var_idx: self.var_idx.clone(),
host: self.host.clone(),
Expand All @@ -210,7 +198,7 @@ impl<F: FnOnce(DynNetMut) + Send + Sync + 'static> ReadbackDef<F> {
impl<F: FnOnce(DynNetMut) + Send + Sync + 'static> AsBoxDef for ReadbackDef<F> {
fn call<M: Mode>(def: Box<Def<Self>>, net: &mut Net<M>, port: Port) {
match port.tag() {
Tag::Red => {
Tag::Var | Tag::Red => {
unreachable!()
}
Tag::Ref if port != Port::ERA => {
Expand All @@ -221,23 +209,18 @@ impl<F: FnOnce(DynNetMut) + Send + Sync + 'static> AsBoxDef for ReadbackDef<F> {
(*def.data.tree.0) = var.clone();
(*other.data.0.tree.0) = var;
}
net.link_port_port(def.data.out, other.data.0.out);
Self::maybe_finish(DynNetMut::from(&mut *net), other.data.0.root);
} else if let Some(back) = def.data.host.lock().unwrap().back.get(&port.addr()) {
unsafe { *(def.data.tree.0) = Tree::Ref { nam: back.clone() } };
net.link_port_port(def.data.out, port);
} else {
unsafe { *(def.data.tree.0) = Tree::Era };
net.link_port_port(def.data.out, port);
}
}
Tag::Ref => {
unsafe { *(def.data.tree.0) = Tree::Era };
net.link_port_port(def.data.out, port);
}
Tag::Num => {
unsafe { *(def.data.tree.0) = Tree::Num { val: port.num() } };
net.link_port_port(def.data.out, port)
}
Tag::Mat => {
unsafe {
Expand All @@ -246,19 +229,13 @@ impl<F: FnOnce(DynNetMut) + Send + Sync + 'static> AsBoxDef for ReadbackDef<F> {
};
let Tree::Mat { zero, succ, out } = (unsafe { &mut *(def.data.tree.0) }) else { unreachable!() };
let old = port.clone().consume_node();
let new = net.create_node(Tag::Mat, old.lab);
let old_sel = old.p1.load_target().consume_node();
let new_sel = net.create_node(Tag::Ctr, old_sel.lab);
net.link_port_port(def.data.out.clone(), new.p0);
net.link_port_port(new.p1, new_sel.p0);
net.link_wire_port(old.p2, def.data.with(out.as_mut(), new.p2));
net.link_wire_port(old_sel.p1, def.data.with(zero.as_mut(), new_sel.p1));
net.link_wire_port(old_sel.p2, def.data.with(succ.as_mut(), new_sel.p2));
net.link_wire_port(old.p2, def.data.with(out.as_mut()));
net.link_wire_port(old_sel.p1, def.data.with(zero.as_mut()));
net.link_wire_port(old_sel.p2, def.data.with(succ.as_mut()));
}
Tag::Var => net.link_port_port(def.data.out, port),
tag @ (Tag::Op | Tag::Ctr) => {
let old = port.clone().consume_node();
let new = net.create_node(tag, old.lab);
let (lhs, rhs): (*mut Tree, *mut Tree) = match tag {
Tag::Op => {
unsafe {
Expand All @@ -274,40 +251,42 @@ impl<F: FnOnce(DynNetMut) + Send + Sync + 'static> AsBoxDef for ReadbackDef<F> {
}
_ => unreachable!(),
};
net.link_port_port(def.data.out.clone(), new.p0);
net.link_wire_port(old.p1, def.data.with(lhs, new.p1));
net.link_wire_port(old.p2, def.data.with(rhs, new.p2));
net.link_wire_port(old.p1, def.data.with(lhs));
net.link_wire_port(old.p2, def.data.with(rhs));
}
}
Self::maybe_finish(DynNetMut::from(net), def.data.root);
}
}

// public api:
// readback: Wire -> FnOnce(Tree) -> Port
// readback_and_wait: Net -> Wire -> Tree

pub fn readback(host: Arc<Mutex<Host>>, out: Port, f: impl FnOnce(DynNetMut, Tree) + Send + Sync + 'static) -> Port {
let root = Box::leak(Box::new(Tree::default()));
pub fn readback<M: Mode>(
net: &mut Net<M>,
host: Arc<Mutex<Host>>,
from: Trg,
f: impl FnOnce(DynNetMut, Tree) + Send + Sync + 'static,
) {
let root = UniqueTreePtr(Box::leak(Box::new(Tree::default())));

let root_ptr = UniqueTreePtr(root);
let root = UniqueTreePtr(root);
if M::LAZY {
let from = net.wire_to_trg(from);
net.normal_from(from.clone());
let tree = host.lock().unwrap().readback_tree(&from);
net.link_wire_port(from, Port::ERA);
f(DynNetMut::from(net), tree);
} else {
let closure: Box<dyn FnOnce(DynNetMut) + Send + Sync + 'static> = Box::new(move |net| {
let root = unsafe { root.to_box() };
f(net, *root);
});

use crate::run;
let closure: Box<
dyn for<'a, 'b> FnOnce(run::DynNetInner<&'a mut run::Net<'b, run::Lazy>, &'a mut run::Net<'b, run::Strict>>)
+ Send
+ Sync
+ 'static,
> = Box::new(move |net| {
let root = unsafe { root.to_box() };
f(net, *root);
});
Port::new_ref(Box::leak(BoxDef::new_boxed(LabSet::ALL, ReadbackDef {
root: Arc::new(closure),
host,
tree: root_ptr,
var_idx: Arc::new(AtomicUsize::from(0)),
out,
})))
net.link_trg_port(
from,
Port::new_ref(Box::leak(BoxDef::new_boxed(LabSet::ALL, ReadbackDef {
root: Arc::new(closure),
host,
tree: root,
var_idx: Arc::new(AtomicUsize::from(0)),
}))),
);
}
}

0 comments on commit 3c414ce

Please sign in to comment.