From cc493ee3143449a43efc0be2d6a8bf99e435dbbb Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 13:19:33 -0400 Subject: [PATCH 1/8] remove lazy mode --- host/src/encode.rs | 8 ++-- host/src/host.rs | 2 +- host/src/readback.rs | 4 +- host/src/stdlib.rs | 78 +++++++++++++++------------------ runtime/src/def.rs | 29 +++++------- runtime/src/dyn_net.rs | 71 ------------------------------ runtime/src/instruction.rs | 22 +++++----- runtime/src/interact.rs | 2 +- runtime/src/linker.rs | 63 +++----------------------- runtime/src/net.rs | 85 +++++------------------------------- runtime/src/node.rs | 2 +- runtime/src/parallel.rs | 16 +++---- runtime/src/runtime.rs | 27 ------------ src/args.rs | 8 ---- src/compile.rs | 2 +- src/compile/include_files.rs | 1 - src/main.rs | 32 +++++++------- tests/cli.rs | 2 +- tests/loaders.rs | 2 +- tests/tests.rs | 2 +- transform/src/pre_reduce.rs | 6 +-- 21 files changed, 109 insertions(+), 355 deletions(-) delete mode 100644 runtime/src/dyn_net.rs diff --git a/host/src/encode.rs b/host/src/encode.rs index a15bf8a8..b98b07d2 100644 --- a/host/src/encode.rs +++ b/host/src/encode.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use crate::Host; use hvm64_ast::{Lab, Net as AstNet, Tree}; -use hvm64_runtime::{Instruction, InterpretedDef, Mode, Net, Port, Trg, TrgId}; +use hvm64_runtime::{Instruction, InterpretedDef, Net, Port, Trg, TrgId}; use hvm64_util::{maybe_grow, ops::TypedOp as Op}; impl Host { @@ -19,7 +19,7 @@ impl Host { /// Encode `tree` directly into `trg`, skipping the intermediate `Def` /// representation. - pub fn encode_tree(&self, net: &mut Net, trg: Trg, tree: &Tree) { + pub fn encode_tree(&self, net: &mut Net, trg: Trg, tree: &Tree) { let mut state = State { host: self, encoder: net, scope: Default::default() }; state.visit_tree(tree, trg); state.finish(); @@ -27,7 +27,7 @@ impl Host { /// Encode the root of `ast_net` directly into `trg` and encode its redexes /// into `net` redex list. - pub fn encode_net(&self, net: &mut Net, trg: Trg, ast_net: &AstNet) { + pub fn encode_net(&self, net: &mut Net, trg: Trg, ast_net: &AstNet) { let mut state = State { host: self, encoder: net, scope: Default::default() }; state.visit_net(ast_net, trg); state.finish(); @@ -193,7 +193,7 @@ impl Encoder for InterpretedDef { } } -impl<'a, M: Mode> Encoder for Net<'a, M> { +impl<'a> Encoder for Net<'a> { type Trg = Trg; fn link_const(&mut self, trg: Self::Trg, port: Port) { diff --git a/host/src/host.rs b/host/src/host.rs index b19d9ecd..1424917d 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -7,7 +7,7 @@ include!("../../prelude.rs"); use crate::prelude::*; use hvm64_ast::{Book, Tree}; -use hvm64_runtime::{Addr, Def, InterpretedDef, LabSet, Mode, Port, Tag, Wire}; +use hvm64_runtime::{Addr, Def, InterpretedDef, LabSet, Port, Tag, Wire}; use core::ops::{Deref, DerefMut}; diff --git a/host/src/readback.rs b/host/src/readback.rs index 62b49d79..eb9753a6 100644 --- a/host/src/readback.rs +++ b/host/src/readback.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -use super::{Addr, Host, Mode, Port, Tag, Wire}; +use super::{Addr, Host, Port, Tag, Wire}; use core::ops::RangeFrom; @@ -19,7 +19,7 @@ impl Host { /// resulting ast net, as it is impossible to read these back from the runtime /// net representation. In the case of vicious circles, this may result in /// unbound variables. - pub fn readback(&self, rt_net: &hvm64_runtime::Net) -> Net { + pub fn readback(&self, rt_net: &hvm64_runtime::Net) -> Net { let mut state = ReadbackState { host: self, vars: Default::default(), var_id: 0 .. }; let mut net = Net::default(); diff --git a/host/src/stdlib.rs b/host/src/stdlib.rs index 7bec4d60..cb144887 100644 --- a/host/src/stdlib.rs +++ b/host/src/stdlib.rs @@ -10,13 +10,13 @@ use parking_lot::Mutex; use crate::{DefRef, Host}; use hvm64_ast::Tree; -use hvm64_runtime::{dispatch_dyn_net, AsDef, Def, DynNetMut, LabSet, Mode, Net, Port, Tag, Trg}; +use hvm64_runtime::{AsDef, Def, LabSet, Net, Port, Tag, Trg}; use hvm64_util::create_var; /// `@IDENTITY = (x x)` -pub const IDENTITY: *const Def = const { &Def::new(LabSet::from_bits(&[1]), (call_identity, call_identity)) }.upcast(); +pub const IDENTITY: *const Def = const { &Def::new(LabSet::from_bits(&[1]), call_identity) }.upcast(); -fn call_identity(net: &mut Net, port: Port) { +fn call_identity(net: &mut Net, port: Port) { let (a, b) = net.do_ctr(0, Trg::port(port)); net.link_trg(a, b); } @@ -41,7 +41,7 @@ impl LogDef { pub struct LogDef(Arc>, F); impl AsHostedDef for LogDef { - fn call(def: &Def, net: &mut Net, port: Port) { + fn call(def: &Def, net: &mut Net, port: Port) { let (arg, seq) = net.do_ctr(0, Trg::port(port)); let seq = net.wire_to_trg(seq); // SAFETY: the function inside `readback` won't live longer than @@ -50,9 +50,7 @@ impl AsHostedDef for LogDef { let def: &'static Def = unsafe { 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_wire_port(seq, Port::new_ref(unsafe { &*IDENTITY })); }); } } @@ -102,11 +100,11 @@ impl BoxDef { } pub trait AsBoxDef: Send + Sync + 'static { - fn call(slf: Box>, net: &mut Net, port: Port); + fn call(slf: Box>, net: &mut Net, port: Port); } impl AsDef for BoxDef { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { + unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { T::call(Box::from_raw(slf as *mut _), net, port) } } @@ -136,11 +134,11 @@ impl ArcDef { } pub trait AsArcDef: Send + Sync + 'static { - fn call(slf: Arc>, net: &mut Net, port: Port); + fn call(slf: Arc>, net: &mut Net, port: Port); } impl AsDef for ArcDef { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { + unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { T::call(Arc::from_raw(slf as *mut _), net, port); } } @@ -162,11 +160,11 @@ impl HostedDef { } pub trait AsHostedDef: Send + Sync + 'static { - fn call(slf: &Def, net: &mut Net, port: Port); + fn call(slf: &Def, net: &mut Net, port: Port); } impl AsDef for HostedDef { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { + unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { T::call((slf as *const Def).as_ref().unwrap(), net, port) } } @@ -182,15 +180,15 @@ impl UniqueTreePtr { } } -pub struct ReadbackDef { +pub struct ReadbackDef { root: Arc, host: Arc>, var_idx: Arc, tree: UniqueTreePtr, } -impl ReadbackDef { - fn maybe_finish(net: DynNetMut<'_, '_>, root: Arc) { +impl ReadbackDef { + fn maybe_finish(net: &mut Net, root: Arc) { let Some(root) = Arc::into_inner(root) else { return }; (root)(net) } @@ -204,8 +202,8 @@ impl ReadbackDef { } } -impl AsBoxDef for ReadbackDef { - fn call(def: Box>, net: &mut Net, port: Port) { +impl AsBoxDef for ReadbackDef { + fn call(def: Box>, net: &mut Net, port: Port) { match port.tag() { Tag::Var | Tag::Red => { unreachable!() @@ -218,7 +216,7 @@ impl AsBoxDef for ReadbackDef { (*def.data.tree.0) = var.clone(); (*other.data.0.tree.0) = var; } - Self::maybe_finish(DynNetMut::from(&mut *net), other.data.0.root); + Self::maybe_finish(net, other.data.0.root); } else if let Some(back) = def.data.host.lock().back.get(&port.addr()) { unsafe { *(def.data.tree.0) = Tree::Ref { nam: back.clone() } }; } else { @@ -267,38 +265,30 @@ impl AsBoxDef for ReadbackDef { net.link_wire_port(old.p2, def.data.with(rhs)); } } - Self::maybe_finish(DynNetMut::from(net), def.data.root); + Self::maybe_finish(net, def.data.root); } } -pub fn readback( - net: &mut Net, +pub fn readback( + net: &mut Net, host: Arc>, from: Trg, - f: impl FnOnce(DynNetMut, Tree) + Send + Sync + 'static, + f: impl FnOnce(&mut Net, Tree) + Send + Sync + 'static, ) { let root = UniqueTreePtr(Box::leak(Box::default())); - if M::LAZY { - let from = net.wire_to_trg(from); - net.normal_from(from.clone()); - let tree = host.lock().readback_tree(&from); - net.link_wire_port(from, Port::ERA); - f(DynNetMut::from(net), tree); - } else { - let closure: Box = Box::new(move |net| { - let root = unsafe { root.to_box() }; - f(net, *root); - }); + let closure: Box = Box::new(move |net| { + let root = unsafe { root.to_box() }; + f(net, *root); + }); - 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)), - }))), - ); - } + 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)), + }))), + ); } diff --git a/runtime/src/def.rs b/runtime/src/def.rs index 652c056e..939714d3 100644 --- a/runtime/src/def.rs +++ b/runtime/src/def.rs @@ -88,8 +88,7 @@ pub struct Def { /// interaction combinator whose label is not in this set. pub labs: LabSet, ty: TypeId, - call_strict: unsafe fn(*const Def, &mut Net, port: Port), - call_lazy: unsafe fn(*const Def, &mut Net, port: Port), + call: unsafe fn(*const Def, &mut Net, port: Port), pub data: T, } @@ -106,7 +105,7 @@ unsafe impl Send for Dynamic {} unsafe impl Sync for Dynamic {} pub trait AsDef: Any + Send + Sync { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port); + unsafe fn call(slf: *const Def, net: &mut Net, port: Port); } impl Def { @@ -114,7 +113,7 @@ impl Def { where T: AsDef, { - Def { labs, ty: TypeId::of::(), call_strict: T::call::, call_lazy: T::call::, data } + Def { labs, ty: TypeId::of::(), call: T::call, data } } #[inline(always)] @@ -146,11 +145,8 @@ impl Def { unsafe { Def::downcast_mut_ptr(self).map(|x| &mut *x) } } #[inline(always)] - pub unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { - match net.as_dyn_mut() { - DynNetMut::Strict(net) => ((*slf).call_strict)(slf as *const _, net, port), - DynNetMut::Lazy(net) => ((*slf).call_lazy)(slf as *const _, net, port), - } + pub unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { + ((*slf).call)(slf as *const _, net, port) } } @@ -169,18 +165,13 @@ impl DerefMut for Def { } } -impl, Port) + Send + Sync + 'static, G: Fn(&mut Net, Port) + Send + Sync + 'static> AsDef - for (F, G) -{ - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { - match net.as_dyn_mut() { - DynNetMut::Strict(net) => ((*slf).data.0)(net, port), - DynNetMut::Lazy(net) => ((*slf).data.1)(net, port), - } +impl AsDef for F { + unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { + ((*slf).data)(net, port) } } -impl<'a, M: Mode> Net<'a, M> { +impl<'a> Net<'a> { /// Expands a [`Ref`] node connected to `trg`. #[inline(never)] pub fn call(&mut self, port: Port, trg: Port) { @@ -223,7 +214,7 @@ impl InterpretedDef { } impl AsDef for InterpretedDef { - unsafe fn call(def: *const Def, net: &mut Net, trg: Port) { + unsafe fn call(def: *const Def, net: &mut Net, trg: Port) { let def = unsafe { &*def }; let def = &def.data; let instructions = &def.instr; diff --git a/runtime/src/dyn_net.rs b/runtime/src/dyn_net.rs deleted file mode 100644 index 52e2a49b..00000000 --- a/runtime/src/dyn_net.rs +++ /dev/null @@ -1,71 +0,0 @@ -use super::{Heap, Lazy, Mode, Net, Strict}; - -use crate::prelude::*; - -/// A [`Net`] whose mode is determined dynamically, at runtime. -/// -/// Use [`dispatch_dyn_net!`] to wrap operations on the inner net. -pub type DynNet<'a> = DynNetInner, Net<'a, Strict>>; - -/// A mutable reference to a [`Net`] whose mode is determined dynamically, at -/// runtime. -/// -/// Use [`dispatch_dyn_net!`] to wrap operations on the inner net. -pub type DynNetMut<'r, 'h> = DynNetInner<&'r mut Net<'h, Lazy>, &'r mut Net<'h, Strict>>; - -pub enum DynNetInner { - Lazy(L), - Strict(S), -} - -impl<'h> DynNet<'h> { - pub fn new(heap: &'h Heap, lazy: bool) -> Self { - if lazy { DynNet::Lazy(Net::new(heap)) } else { DynNet::Strict(Net::new(heap)) } - } -} - -impl<'r, 'h, M: Mode> From<&'r mut Net<'h, M>> for DynNetMut<'r, 'h> { - fn from(value: &'r mut Net<'h, M>) -> Self { - value.as_dyn_mut() - } -} - -impl<'h, M: Mode> Net<'h, M> { - pub fn as_dyn_mut(&mut self) -> DynNetMut<'_, 'h> { - if M::LAZY { - DynNetMut::Lazy(unsafe { mem::transmute(self) }) - } else { - DynNetMut::Strict(unsafe { mem::transmute(self) }) - } - } - - pub fn into_dyn(self) -> DynNet<'h> { - if M::LAZY { - DynNet::Lazy(unsafe { mem::transmute(self) }) - } else { - DynNet::Strict(unsafe { mem::transmute(self) }) - } - } -} - -#[macro_export] -macro_rules! dispatch_dyn_net { - ($pat:pat = $expr:expr => $body:expr) => { - match $expr { - $crate::DynNetInner::Lazy($pat) => $body, - $crate::DynNetInner::Strict($pat) => $body, - } - }; - ($net:ident => $body:expr) => { - dispatch_dyn_net! { $net = $net => $body } - }; - (mut $net:ident => $body:expr) => { - dispatch_dyn_net! { mut $net = $net => $body } - }; - (&$net:ident => $body:expr) => { - dispatch_dyn_net! { $net = &$net => $body } - }; - (&mut $net:ident => $body:expr) => { - dispatch_dyn_net! { $net = &mut $net => $body } - }; -} diff --git a/runtime/src/instruction.rs b/runtime/src/instruction.rs index 61d46d76..1c100846 100644 --- a/runtime/src/instruction.rs +++ b/runtime/src/instruction.rs @@ -107,20 +107,20 @@ impl fmt::Debug for TrgId { } } -impl<'a, M: Mode> Net<'a, M> { +impl<'a> Net<'a> { /// `trg ~ {#lab x y}` #[inline(always)] pub fn do_ctr(&mut self, lab: Lab, trg: Trg) -> (Trg, Trg) { let port = trg.target(); #[allow(clippy::overly_complex_bool_expr)] - if !M::LAZY && port.tag() == Ctr && port.lab() == lab { + if port.tag() == Ctr && port.lab() == lab { trace!(self.tracer, "fast"); self.free_trg(trg); let node = port.consume_node(); self.rwts.anni += 1; (Trg::wire(node.p1), Trg::wire(node.p2)) // TODO: fast copy? - } else if false && !M::LAZY && (port.is_num() || port.tag() == Ref && lab >= port.lab()) { + } else if false && (port.is_num() || port.tag() == Ref && lab >= port.lab()) { self.rwts.comm += 1; self.free_trg(trg); (Trg::port(port.clone()), Trg::port(port)) @@ -136,12 +136,12 @@ impl<'a, M: Mode> Net<'a, M> { pub fn do_op(&mut self, op: Op, trg: Trg) -> (Trg, Trg) { trace!(self.tracer, op, trg); let port = trg.target(); - if !M::LAZY && port.is_num() { + if port.is_num() { self.free_trg(trg); let n = self.create_node(Op, op.swap().into()); n.p1.wire().set_target(port); (Trg::port(n.p0), Trg::port(n.p2)) - } else if !M::LAZY && port == Port::ERA { + } else if port == Port::ERA { self.free_trg(trg); (Trg::port(Port::ERA), Trg::port(Port::ERA)) } else { @@ -155,14 +155,14 @@ impl<'a, M: Mode> Net<'a, M> { #[inline(always)] pub fn do_op_num(&mut self, op: Op, trg: Trg, rhs: Port) -> Trg { let port = trg.target(); - if !M::LAZY && port.is_num() { + if port.is_num() { self.rwts.oper += 1; self.free_trg(trg); let res = op.op(port.num(), rhs.num()); Trg::port(Port::new_num(if op.is_int() { Tag::Int } else { Tag::F32 }, res)) - } else if !M::LAZY && port == Port::ERA { + } else if port == Port::ERA { self.free_trg(trg); Trg::port(Port::ERA) } else { @@ -177,7 +177,7 @@ impl<'a, M: Mode> Net<'a, M> { #[inline(always)] pub fn do_mat(&mut self, trg: Trg) -> (Trg, Trg) { let port = trg.target(); - if !M::LAZY && port.tag() == Int { + if port.tag() == Int { self.rwts.oper += 1; self.free_trg(trg); let num = port.int(); @@ -192,7 +192,7 @@ impl<'a, M: Mode> Net<'a, M> { self.link_port_port(c2.p1, Port::new_int(num - 1)); (Trg::port(c1.p0), Trg::wire(self.create_wire_to(c2.p2))) } - } else if !M::LAZY && port == Port::ERA { + } else if port == Port::ERA { self.rwts.eras += 1; self.free_trg(trg); (Trg::port(Port::ERA), Trg::port(Port::ERA)) @@ -215,7 +215,7 @@ impl<'a, M: Mode> Net<'a, M> { #[allow(unused)] // TODO: emit this instruction pub fn do_mat_con_con(&mut self, trg: Trg, out: Trg) -> (Trg, Trg, Trg) { let port = trg.target(); - if !M::LAZY && trg.target().tag() == Int { + if trg.target().tag() == Int { self.rwts.oper += 1; self.free_trg(trg); let num = port.int(); @@ -224,7 +224,7 @@ impl<'a, M: Mode> Net<'a, M> { } else { (Trg::port(Port::ERA), Trg::port(Port::new_int(num - 1)), out) } - } else if !M::LAZY && port == Port::ERA { + } else if port == Port::ERA { self.link_trg_port(out, Port::ERA); (Trg::port(Port::ERA), Trg::port(Port::ERA), Trg::port(Port::ERA)) } else { diff --git a/runtime/src/interact.rs b/runtime/src/interact.rs index 973b3068..c841e0a6 100644 --- a/runtime/src/interact.rs +++ b/runtime/src/interact.rs @@ -1,6 +1,6 @@ use super::*; -impl<'a, M: Mode> Net<'a, M> { +impl<'a> Net<'a> { /// Performs an interaction between two connected principal ports. #[inline(always)] pub fn interact(&mut self, a: Port, b: Port) { diff --git a/runtime/src/linker.rs b/runtime/src/linker.rs index 61da3084..93823518 100644 --- a/runtime/src/linker.rs +++ b/runtime/src/linker.rs @@ -1,19 +1,5 @@ use super::*; -#[cfg(not(feature = "std"))] -use crate::prelude::Map as IntMap; -#[cfg(feature = "std")] -use nohash_hasher::IntMap; - -/// Stores extra data needed about the nodes when in lazy mode. (In strict mode, -/// this is unused.) -pub(super) struct Header { - /// the principal port of this node - pub(super) this: Port, - /// the port connected to the principal port of this node - pub(super) targ: Port, -} - /// Manages linking ports and wires within the net. /// /// When threads interfere, this uses the atomic linking algorithm described in @@ -21,25 +7,17 @@ pub(super) struct Header { /// /// Linking wires must be done atomically, but linking ports can be done /// non-atomically (because they must be locked). -pub struct Linker<'h, M: Mode> { +pub struct Linker<'h> { pub(super) allocator: Allocator<'h>, pub rwts: Rewrites, pub redexes: RedexQueue, - headers: IntMap, - _mode: PhantomData, } -deref!({<'h, M: Mode>} Linker<'h, M> => self.allocator: Allocator<'h>); +deref!({<'h, >} Linker<'h> => self.allocator: Allocator<'h>); -impl<'h, M: Mode> Linker<'h, M> { +impl<'h> Linker<'h> { pub fn new(heap: &'h Heap) -> Self { - Linker { - allocator: Allocator::new(heap), - redexes: RedexQueue::default(), - rwts: Default::default(), - headers: Default::default(), - _mode: PhantomData, - } + Linker { allocator: Allocator::new(heap), redexes: RedexQueue::default(), rwts: Default::default() } } /// Links two ports. @@ -94,7 +72,7 @@ impl<'h, M: Mode> Linker<'h, M> { debug_assert!(!(a.is(Tag::Var) || a.is(Tag::Red) || b.is(Tag::Var) || b.is(Tag::Red))); if a.is_skippable() && b.is_skippable() { self.rwts.eras += 1; - } else if !M::LAZY { + } else { // Prioritize redexes that do not allocate memory, // to prevent OOM errors that can be avoided // by reducing redexes in a different order (see #91) @@ -103,9 +81,6 @@ impl<'h, M: Mode> Linker<'h, M> { } else { self.redexes.slow.push((a, b)); } - } else { - self.set_header(a.clone(), b.clone()); - self.set_header(b.clone(), a.clone()); } } @@ -116,8 +91,6 @@ impl<'h, M: Mode> Linker<'h, M> { trace!(self, a_port, b_port); if a_port.is(Tag::Var) { a_port.wire().set_target(b_port); - } else if M::LAZY { - self.set_header(a_port, b_port); } } @@ -150,9 +123,6 @@ impl<'h, M: Mode> Linker<'h, M> { } } else { self.free_wire(a_wire); - if M::LAZY { - self.set_header(a_port, b_port); - } } } @@ -310,7 +280,7 @@ impl Trg { } } -impl<'h, M: Mode> Linker<'h, M> { +impl<'h> Linker<'h> { /// Links a `Trg` to a port, delegating to the appropriate method based on the /// type of `a`. #[inline(always)] @@ -332,27 +302,6 @@ impl<'h, M: Mode> Linker<'h, M> { (false, false) => self.link_port_port(a.as_port(), b.as_port()), } } - - pub(super) fn get_header(&self, addr: Addr) -> &Header { - assert!(M::LAZY); - &self.headers[&addr] - } - - pub(super) fn set_header(&mut self, ptr: Port, trg: Port) { - assert!(M::LAZY); - trace!(self, ptr, trg); - if ptr.is_full_node() { - self.headers.insert(ptr.addr(), Header { this: ptr, targ: trg }); - } - } - - pub(super) fn get_target_full(&self, port: Port) -> Port { - assert!(M::LAZY); - if !port.is_principal() { - return port.wire().load_target(); - } - self.headers[&port.addr()].targ.clone() - } } #[derive(Debug, Default)] diff --git a/runtime/src/net.rs b/runtime/src/net.rs index 67365f3d..8ca66e34 100644 --- a/runtime/src/net.rs +++ b/runtime/src/net.rs @@ -3,17 +3,17 @@ use super::*; use mem::MaybeUninit; /// An interaction combinator net. -pub struct Net<'a, M: Mode> { - pub(super) linker: Linker<'a, M>, +pub struct Net<'a> { + pub(super) linker: Linker<'a>, pub tid: usize, // thread id pub tids: usize, // thread count pub trgs: Box<[MaybeUninit]>, pub root: Wire, } -deref!({<'a, M: Mode>} Net<'a, M> => self.linker: Linker<'a, M>); +deref!({<'a, >} Net<'a> => self.linker: Linker<'a>); -impl<'h, M: Mode> Net<'h, M> { +impl<'h> Net<'h> { /// Creates an empty net with a given heap. pub fn new(heap: &'h Heap) -> Self { let mut net = Net::new_with_root(heap, Wire(ptr::null())); @@ -31,14 +31,13 @@ impl<'h, M: Mode> Net<'h, M> { } } -impl<'a, M: Mode> Net<'a, M> { +impl<'a> Net<'a> { /// Reduces at most `limit` redexes. /// /// If normalized, returns `Some(num_redexes)`. /// If stopped because the limit was reached, returns `None`. #[inline(always)] pub fn reduce(&mut self, limit: usize) -> Option { - assert!(!M::LAZY); let mut count = 0; while let Some((a, b)) = self.redexes.pop() { @@ -51,82 +50,18 @@ impl<'a, M: Mode> Net<'a, M> { Some(count) } - // Lazy mode weak head normalizer - #[inline(always)] - pub fn weak_normal(&mut self, mut prev: Port, root: Wire) -> Port { - assert!(M::LAZY); - - let mut path: Vec = vec![]; - - loop { - trace!(self.tracer, prev); - // Load ptrs - let next = self.get_target_full(prev.clone()); - trace!(self.tracer, next); - - // If next is root, stop. - if next == Port::new_var(root.addr()) || next == Port::new_var(self.root.addr()) { - break; - } - - // If next is a main port... - if next.is_principal() { - // If prev is a main port, reduce the active pair. - if prev.is_principal() { - self.interact(next, prev.clone()); - prev = path.pop().unwrap(); - continue; - // Otherwise, if it is a ref, expand it. - } else if next.tag() == Ref && next != Port::ERA { - self.call(next, prev.clone()); - continue; - // Otherwise, we're done. - } else { - break; - } - } - - // If next is an aux port, pass through. - let main = self.get_header(next.addr().left_half()); - path.push(prev); - prev = main.this.clone(); - } - - self.get_target_full(prev) - } - - pub fn normal_from(&mut self, root: Wire) { - assert!(M::LAZY); - let mut visit = vec![Port::new_var(root.addr())]; - while let Some(prev) = visit.pop() { - trace!(self.tracer, "visit", prev); - //println!("normal {} | {}", prev.view(), self.rewrites()); - let next = self.weak_normal(prev, root.clone()); - trace!(self.tracer, "got", next); - if next.is_full_node() { - visit.push(Port::new_var(next.addr())); - visit.push(Port::new_var(next.addr().other_half())); - } - } - } - /// Reduces a net to normal form. pub fn normal(&mut self) { - if M::LAZY { - self.normal_from(self.root.clone()); - } else { - self.expand(); - while !self.redexes.is_empty() { - self.reduce(usize::MAX); - } + self.expand(); + while !self.redexes.is_empty() { + self.reduce(usize::MAX); } } } -impl<'h, M: Mode> Net<'h, M> { +impl<'h> Net<'h> { /// Expands [`Tag::Ref`] nodes in the tree connected to `root`. pub fn expand(&mut self) { - assert!(!M::LAZY); let (new_root, out_port) = self.create_wire(); let old_root = mem::replace(&mut self.root, new_root); self.link_wire_port(old_root, ExpandDef::new(out_port)); @@ -144,7 +79,7 @@ impl ExpandDef { } impl AsDef for ExpandDef { - unsafe fn call(def: *const Def, net: &mut Net, port: Port) { + unsafe fn call(def: *const Def, net: &mut Net, port: Port) { if port.tag() == Tag::Ref && port != Port::ERA { let other: *const Def = port.addr().def() as *const _; if let Some(other) = Def::downcast_ptr::(other) { diff --git a/runtime/src/node.rs b/runtime/src/node.rs index 7fdec7e3..1e87668e 100644 --- a/runtime/src/node.rs +++ b/runtime/src/node.rs @@ -31,7 +31,7 @@ pub struct CreatedNode { pub p2: Port, } -impl<'a, M: Mode> Net<'a, M> { +impl<'a> Net<'a> { #[inline(always)] pub fn create_node(&mut self, tag: Tag, lab: Lab) -> CreatedNode { let addr = self.alloc(); diff --git a/runtime/src/parallel.rs b/runtime/src/parallel.rs index 5c7babb8..5517b489 100644 --- a/runtime/src/parallel.rs +++ b/runtime/src/parallel.rs @@ -7,7 +7,7 @@ use atomic::AtomicUsize; use super::*; -impl<'h, M: Mode> Net<'h, M> { +impl<'h> Net<'h> { /// Forks the net into `tids` child nets, for parallel operation. pub fn fork(&mut self, tids: usize) -> impl Iterator + '_ { let redexes_len = self.linker.redexes.len(); @@ -33,19 +33,17 @@ impl<'h, M: Mode> Net<'h, M> { // Evaluates a term to normal form in parallel pub fn parallel_normal(&mut self) { - assert!(!M::LAZY); - self.expand(); const SHARE_LIMIT: usize = 1 << 12; // max share redexes per split const LOCAL_LIMIT: usize = 1 << 18; // max local rewrites per epoch // Local thread context - struct ThreadContext<'a, M: Mode> { + struct ThreadContext<'a> { tid: usize, // thread id tlog2: usize, // log2 of thread count tick: usize, // current tick - net: Net<'a, M>, // thread's own net object + net: Net<'a>, // thread's own net object delta: &'a AtomicRewrites, // global delta rewrites share: &'a Vec<(AtomicU64, AtomicU64)>, // global share buffer rlens: &'a Vec, // global redex lengths (only counting shareable ones) @@ -85,7 +83,7 @@ impl<'h, M: Mode> Net<'h, M> { // Main reduction loop #[inline(always)] - fn main(ctx: &mut ThreadContext) { + fn main(ctx: &mut ThreadContext) { loop { reduce(ctx); if count(ctx) == 0 { @@ -97,7 +95,7 @@ impl<'h, M: Mode> Net<'h, M> { // Reduce redexes locally, then share with target #[inline(always)] - fn reduce(ctx: &mut ThreadContext) { + fn reduce(ctx: &mut ThreadContext) { loop { ctx.net.reduce(LOCAL_LIMIT); if count(ctx) == 0 { @@ -111,7 +109,7 @@ impl<'h, M: Mode> Net<'h, M> { // Count total redexes (and populate 'rlens') #[inline(always)] - fn count(ctx: &mut ThreadContext) -> usize { + fn count(ctx: &mut ThreadContext) -> usize { ctx.barry.wait(); ctx.total.store(0, Relaxed); ctx.barry.wait(); @@ -123,7 +121,7 @@ impl<'h, M: Mode> Net<'h, M> { // Share redexes with target thread #[inline(always)] - fn split(ctx: &mut ThreadContext, plog2: usize) { + fn split(ctx: &mut ThreadContext, plog2: usize) { unsafe { let side = (ctx.tid >> (plog2 - 1 - (ctx.tick % plog2))) & 1; let shift = (1 << (plog2 - 1)) >> (ctx.tick % plog2); diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index ffe1e31e..7b35dcf7 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -34,7 +34,6 @@ use core::{ alloc::Layout, any::{Any, TypeId}, hint::unreachable_unchecked, - marker::PhantomData, mem::size_of, ops::{Add, AddAssign, Deref, DerefMut}, }; @@ -51,7 +50,6 @@ use Tag::*; mod addr; mod allocator; mod def; -mod dyn_net; mod instruction; mod interact; mod linker; @@ -65,7 +63,6 @@ mod wire; pub use addr::*; pub use allocator::*; pub use def::*; -pub use dyn_net::*; pub use instruction::*; pub use linker::*; pub use net::*; @@ -75,30 +72,6 @@ pub use wire::*; pub type Lab = u16; -/// The runtime mode is represented with a generic such that, instead of -/// repeatedly branching on the mode at runtime, the branch can happen at the -/// top-most level, and delegate to monomorphized functions specialized for each -/// particular mode. -/// -/// This trait is `unsafe` as it may only be implemented by [`Strict`] and -/// [`Lazy`]. -pub unsafe trait Mode: Send + Sync + 'static { - const LAZY: bool; -} - -/// In strict mode, all active pairs are expanded. -pub struct Strict; -unsafe impl Mode for Strict { - const LAZY: bool = false; -} - -/// In lazy mode, only active pairs that are reached from a walk from the root -/// port are expanded. -pub struct Lazy; -unsafe impl Mode for Lazy { - const LAZY: bool = true; -} - /// Tracks the number of rewrites, categorized by type. #[derive(Clone, Copy, Debug, Default)] pub struct Rewrites { diff --git a/src/args.rs b/src/args.rs index 8d62a1cc..f7c2bd14 100644 --- a/src/args.rs +++ b/src/args.rs @@ -27,14 +27,6 @@ pub struct RuntimeOpts { #[arg(short = '1', long = "single")] pub single_core: bool, - /// Lazy mode. - /// - /// Lazy mode only expands references that are reachable - /// by a walk from the root of the net. This leads to a dramatic slowdown, - /// but allows running programs that would expand indefinitely otherwise. - #[arg(short, long = "lazy")] - pub lazy_mode: bool, - /// How much memory to allocate on startup. /// /// Supports abbreviations such as '4G' or '400M'. diff --git a/src/compile.rs b/src/compile.rs index c6909b9b..b71515a4 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -122,7 +122,7 @@ fn compile_struct(code: &mut String, host: &Host, rust_name: &str, def: &Def(slf: *const Def, net: &mut Net, port: Port) {{")?; + writeln!(code, " unsafe fn call(slf: *const Def, net: &mut Net, port: Port) {{")?; writeln!(code, " let slf = unsafe {{ &*slf }};")?; writeln!(code, " let t0 = Trg::port(port);")?; diff --git a/src/compile/include_files.rs b/src/compile/include_files.rs index 8a9dbfc9..fa70c973 100644 --- a/src/compile/include_files.rs +++ b/src/compile/include_files.rs @@ -95,7 +95,6 @@ hvm64-runtime = { path = "../runtime", default-features = false } addr allocator def - dyn_net instruction interact linker diff --git a/src/main.rs b/src/main.rs index 6e6ebc2d..eecacd59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,7 @@ use hvm64_host::{ stdlib::{create_host, insert_stdlib}, DefRef, Host, }; -use hvm64_runtime::{dispatch_dyn_net, Def, DynNet, Heap, Mode, Port, Trg}; +use hvm64_runtime::{Def, Heap, Port, Trg}; use hvm64_transform::Transform; fn main() { @@ -184,25 +184,23 @@ fn load_dylibs(host: Arc>, include: &[PathBuf]) { fn reduce_exprs(host: Arc>, exprs: &[Net], opts: &RuntimeOpts) { let heap = Heap::new(opts.memory).expect("memory allocation failed"); for expr in exprs { - let mut net = DynNet::new(&heap, opts.lazy_mode); - dispatch_dyn_net!(&mut net => { - host.lock().encode_net(net, Trg::port(Port::new_var(net.root.addr())), expr); - let start_time = Instant::now(); - if opts.single_core { - net.normal(); - } else { - net.parallel_normal(); - } - let elapsed = start_time.elapsed(); - println!("{}", host.lock().readback(net)); - if opts.show_stats { - print_stats(net, elapsed); - } - }); + let net = &mut hvm64_runtime::Net::new(&heap); + host.lock().encode_net(net, Trg::port(Port::new_var(net.root.addr())), expr); + let start_time = Instant::now(); + if opts.single_core { + net.normal(); + } else { + net.parallel_normal(); + } + let elapsed = start_time.elapsed(); + println!("{}", host.lock().readback(net)); + if opts.show_stats { + print_stats(net, elapsed); + } } } -fn print_stats(net: &hvm64_runtime::Net, elapsed: Duration) { +fn print_stats(net: &hvm64_runtime::Net, elapsed: Duration) { eprintln!("RWTS : {:>15}", pretty_num(net.rwts.total())); eprintln!("- ANNI : {:>15}", pretty_num(net.rwts.anni)); eprintln!("- COMM : {:>15}", pretty_num(net.rwts.comm)); diff --git a/tests/cli.rs b/tests/cli.rs index c820dfdb..7e06e36b 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -209,7 +209,7 @@ fn test_apply_tree() { } let host = Host::default(); - let mut rnet = run::Net::::new(&area); + let mut rnet = run::Net::new(&area); let root_port = run::Trg::port(run::Port::new_var(rnet.root.addr())); host.encode_net(&mut rnet, root_port, &fun); rnet.normal(); diff --git a/tests/loaders.rs b/tests/loaders.rs index 1101b078..533c17ce 100644 --- a/tests/loaders.rs +++ b/tests/loaders.rs @@ -29,7 +29,7 @@ pub fn normal_with(book: Book, mem: Option, entry_point: &str) -> (run::R let area = run::Heap::new(mem).unwrap(); let host = create_host(&book); - let mut rnet = run::Net::::new(&area); + let mut rnet = run::Net::new(&area); rnet.boot(&host.lock().defs[entry_point]); rnet.normal(); diff --git a/tests/tests.rs b/tests/tests.rs index 708058ea..26446e0c 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -21,7 +21,7 @@ use serial_test::serial; fn execute_host(host: Arc>) -> Option<(run::Rewrites, Net)> { let heap = run::Heap::new(None).unwrap(); - let mut net = run::Net::::new(&heap); + let mut net = run::Net::new(&heap); // The host is locked inside this block. { let lock = host.lock(); diff --git a/transform/src/pre_reduce.rs b/transform/src/pre_reduce.rs index 785c695a..fd5f1885 100644 --- a/transform/src/pre_reduce.rs +++ b/transform/src/pre_reduce.rs @@ -20,7 +20,7 @@ use hvm64_host::{ stdlib::{AsHostedDef, HostedDef}, DefRef, Host, }; -use hvm64_runtime::{Def, Heap, InterpretedDef, LabSet, Mode, Port, Rewrites, Strict}; +use hvm64_runtime::{Def, Heap, InterpretedDef, LabSet, Port, Rewrites}; use hvm64_util::maybe_grow; use alloc::sync::Arc; @@ -93,7 +93,7 @@ enum SeenState { struct InertDef(Arc>>); impl AsHostedDef for InertDef { - fn call(def: &Def, _: &mut hvm64_runtime::Net, port: Port) { + fn call(def: &Def, _: &mut hvm64_runtime::Net, port: Port) { def.data.0.lock().push((Port::new_ref(def), port)); } } @@ -139,7 +139,7 @@ impl<'a> State<'a> { // First, pre-reduce all nets referenced by this net by walking the tree self.visit_net(self.book.get(nam).unwrap()); - let mut rt = hvm64_runtime::Net::::new(self.area); + let mut rt = hvm64_runtime::Net::new(self.area); rt.boot(self.host.defs.get(nam).expect("No function.")); let n_reduced = rt.reduce(self.max_rwts as usize); From 06f5fbb54ccfa9e738f5eefe239d12060a282f5a Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 14:09:05 -0400 Subject: [PATCH 2/8] remove nohash_hasher dep --- Cargo.lock | 7 ------- cspell.json | 1 - runtime/Cargo.toml | 3 +-- runtime/src/addr.rs | 3 --- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c99187a..1a58a374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,6 @@ name = "hvm64-runtime" version = "0.3.0" dependencies = [ "hvm64-util", - "nohash-hasher", "parking_lot", ] @@ -414,12 +413,6 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - [[package]] name = "num-traits" version = "0.2.18" diff --git a/cspell.json b/cspell.json index e7549f74..cf312877 100644 --- a/cspell.json +++ b/cspell.json @@ -31,7 +31,6 @@ "monomorphized", "newtype", "nilary", - "nohash", "nomicon", "oper", "outdir", diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 549130d5..9b96d848 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -8,12 +8,11 @@ path = "src/runtime.rs" [dependencies] parking_lot = "0.12.2" -nohash-hasher = { version = "0.2.0", optional = true } hvm64-util = { path = "../util", default-features = false } [features] -std = ["hvm64-util/std", "dep:nohash-hasher"] +std = ["hvm64-util/std"] trace = [] [lints] diff --git a/runtime/src/addr.rs b/runtime/src/addr.rs index b0adf117..edcb7d4d 100644 --- a/runtime/src/addr.rs +++ b/runtime/src/addr.rs @@ -12,9 +12,6 @@ use super::*; #[must_use] pub struct Addr(pub usize); -#[cfg(feature = "std")] -impl nohash_hasher::IsEnabled for Addr {} - impl fmt::Debug for Addr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:012x?}", self.0) From 0d5dc5cedb5040969ae5eb864b275da8a3f5fbc1 Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 13:49:38 -0400 Subject: [PATCH 3/8] remove io --- Cargo.lock | 4 - Cargo.toml | 1 - host/Cargo.toml | 2 - host/src/host.rs | 37 +-- host/src/stdlib.rs | 294 ------------------ runtime/Cargo.toml | 5 +- runtime/src/def.rs | 2 + runtime/src/trace.rs | 15 +- src/compile.rs | 3 +- src/compile/include_files.rs | 8 +- src/main.rs | 47 +-- tests/cli.rs | 14 - tests/loaders.rs | 8 +- tests/programs/heavy_pre_reduction.bend | 8 +- tests/programs/heavy_pre_reduction.hvm | 5 +- tests/programs/log.bend | 1 - tests/programs/log.hvm | 3 - ...re_reduce_run@heavy_pre_reduction.hvm.snap | 6 +- .../tests__pre_reduce_run@log.hvm.snap | 20 -- tests/snapshots/tests__run@log.hvm.snap | 12 - tests/tests.rs | 32 +- tests/transform.rs | 6 +- transform/Cargo.toml | 1 - transform/src/pre_reduce.rs | 45 +-- 24 files changed, 96 insertions(+), 483 deletions(-) delete mode 100644 host/src/stdlib.rs delete mode 100644 tests/programs/log.bend delete mode 100644 tests/programs/log.hvm delete mode 100644 tests/snapshots/tests__pre_reduce_run@log.hvm.snap delete mode 100644 tests/snapshots/tests__run@log.hvm.snap diff --git a/Cargo.lock b/Cargo.lock index 1a58a374..0b88971e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -288,7 +288,6 @@ dependencies = [ "insta", "libloading", "ordered-float", - "parking_lot", "serial_test", "thiserror", ] @@ -311,7 +310,6 @@ dependencies = [ "hvm64-ast", "hvm64-runtime", "hvm64-util", - "parking_lot", ] [[package]] @@ -319,7 +317,6 @@ name = "hvm64-runtime" version = "0.3.0" dependencies = [ "hvm64-util", - "parking_lot", ] [[package]] @@ -331,7 +328,6 @@ dependencies = [ "hvm64-runtime", "hvm64-util", "ordered-float", - "parking_lot", "thiserror", ] diff --git a/Cargo.toml b/Cargo.toml index 26649ed1..c25e4188 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ debug = "full" clap = { version = "4.5.4", features = ["derive"] } libloading = { version = "0.8.3", default-features = false } ordered-float = { version = "4.2.0" } -parking_lot = "0.12.2" thiserror = "1.0.58" hvm64-ast = { path = "./ast" } diff --git a/host/Cargo.toml b/host/Cargo.toml index 43812eac..2aebbca7 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -7,8 +7,6 @@ edition = "2021" path = "src/host.rs" [dependencies] -parking_lot = "0.12.2" - hvm64-ast = { path = "../ast", default-features = false } hvm64-util = { path = "../util", default-features = false } hvm64-runtime = { path = "../runtime", default-features = false } diff --git a/host/src/host.rs b/host/src/host.rs index 1424917d..6f89de15 100644 --- a/host/src/host.rs +++ b/host/src/host.rs @@ -7,11 +7,7 @@ include!("../../prelude.rs"); use crate::prelude::*; use hvm64_ast::{Book, Tree}; -use hvm64_runtime::{Addr, Def, InterpretedDef, LabSet, Port, Tag, Wire}; - -use core::ops::{Deref, DerefMut}; - -pub mod stdlib; +use hvm64_runtime::{Addr, Def, DynDef, InterpretedDef, LabSet, Port, Tag, Wire}; mod calc_labels; mod encode; @@ -23,29 +19,11 @@ use calc_labels::calculate_label_sets; #[derive(Default)] pub struct Host { /// the forward mapping, from a name to the runtime def - pub defs: Map, + pub defs: Map>, /// the backward mapping, from the address of a runtime def to the name pub back: Map, } -/// A potentially-owned reference to a [`Def`]. Vitally, the address of the -/// `Def` is stable, even if the `DefRef` moves -- this is why -/// [`std::Borrow::Cow`] cannot be used here. -pub enum DefRef { - Owned(Box + Send + Sync>), - Static(&'static Def), -} - -impl Deref for DefRef { - type Target = Def; - fn deref(&self) -> &Def { - match self { - DefRef::Owned(x) => x, - DefRef::Static(x) => x, - } - } -} - impl Host { pub fn new(book: &Book) -> Host { let mut host = Host::default(); @@ -63,7 +41,7 @@ impl Host { /// Like `insert_book`, but allows specifying a function (`default_def`) that /// will be run when the name of a definition is not found in the book. /// The return value of the function will be inserted into the host. - pub fn insert_book_with_default(&mut self, book: &Book, default_def: &mut dyn FnMut(&str) -> DefRef) { + pub fn insert_book_with_default(&mut self, book: &Book, default_def: &mut dyn FnMut(&str) -> Box) { #[cfg(feature = "std")] { self.defs.reserve(book.len()); @@ -85,7 +63,7 @@ impl Host { }) .into_iter() { - let def = DefRef::Owned(Box::new(Def::new(labs, InterpretedDef::default()))); + let def = Box::new(Def::new(labs, InterpretedDef::default())); self.insert_def(name, def); } @@ -98,16 +76,13 @@ impl Host { } /// Inserts a singular def into the mapping. - pub fn insert_def(&mut self, name: &str, def: DefRef) { + pub fn insert_def(&mut self, name: &str, def: Box) { self.back.insert(Port::new_ref(&def).addr(), name.to_owned()); self.defs.insert(name.to_owned(), def); } /// Returns a mutable [`Def`] named `name`. pub fn get_mut(&mut self, name: &str) -> &mut Def { - match self.defs.get_mut(name).unwrap() { - DefRef::Owned(def) => def.downcast_mut().unwrap(), - DefRef::Static(_) => unreachable!(), - } + self.defs.get_mut(name).unwrap().downcast_mut().unwrap() } } diff --git a/host/src/stdlib.rs b/host/src/stdlib.rs deleted file mode 100644 index cb144887..00000000 --- a/host/src/stdlib.rs +++ /dev/null @@ -1,294 +0,0 @@ -use crate::prelude::*; - -use alloc::sync::Arc; -use core::{ - marker::PhantomData, - sync::atomic::{AtomicUsize, Ordering}, -}; - -use parking_lot::Mutex; - -use crate::{DefRef, Host}; -use hvm64_ast::Tree; -use hvm64_runtime::{AsDef, Def, LabSet, Net, Port, Tag, Trg}; -use hvm64_util::create_var; - -/// `@IDENTITY = (x x)` -pub const IDENTITY: *const Def = const { &Def::new(LabSet::from_bits(&[1]), call_identity) }.upcast(); - -fn call_identity(net: &mut Net, port: Port) { - let (a, b) = net.do_ctr(0, Trg::port(port)); - net.link_trg(a, b); -} - -/// The definition of `HVM.log`, parameterized by the readback function. -/// -/// `@HVM.log ~ (arg out)` waits for `arg` to be normalized, prints it using the -/// supplied readback function, and then reduces to `arg ~ * & out ~ @IDENTITY`. -/// -/// The output can therefore either be erased, or used to sequence other -/// operations after the log. - -impl LogDef { - /// # SAFETY - /// The caller must ensure that the returned value lives at least as long as - /// the port where it is used. - pub unsafe fn new(host: Arc>, f: F) -> DefRef { - HostedDef::new_hosted(LabSet::ALL, LogDef(host, f)) - } -} - -pub struct LogDef(Arc>, F); - -impl AsHostedDef for LogDef { - fn call(def: &Def, net: &mut Net, port: Port) { - let (arg, seq) = net.do_ctr(0, Trg::port(port)); - 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` points to - let def: &'static Def = unsafe { mem::transmute(def) }; - readback(net, def.data.0.clone(), arg, |net, tree| { - (def.data.1)(tree); - net.link_wire_port(seq, Port::new_ref(unsafe { &*IDENTITY })); - }); - } -} - -/// Create a `Host` from a `Book`, including `hvm-64`'s built-in definitions -#[cfg(feature = "std")] -#[allow(clippy::absolute_paths)] -pub fn create_host(book: &hvm64_ast::Book) -> Arc> { - let host: Arc> = Default::default(); - - insert_stdlib(host.clone()); - host.lock().insert_book(book); - - host -} - -/// Insert `hvm-64`'s built-in definitions into a host. -#[cfg(feature = "std")] -#[allow(clippy::absolute_paths)] -pub fn insert_stdlib(host: Arc>) { - host.lock().insert_def("HVM.log", unsafe { - crate::stdlib::LogDef::new(host.clone(), { - move |tree| { - println!("{}", tree); - } - }) - }); - host.lock().insert_def("HVM.black_box", DefRef::Static(unsafe { &*IDENTITY })); -} - -#[repr(transparent)] -pub struct BoxDef(pub T, PhantomData<()>); - -impl BoxDef { - pub fn new_boxed(labs: LabSet, data: T) -> Box> { - Box::new(Def::new(labs, BoxDef(data, PhantomData))) - } - /// SAFETY: if port is a ref, it must be a valid pointer. - pub unsafe fn try_downcast_box(port: Port) -> Option>> { - if port.is(Tag::Ref) { - unsafe { Def::downcast_ptr::(port.addr().0 as *const _) } - .map(|port| unsafe { Box::from_raw(port as *mut Def) }) - } else { - None - } - } -} - -pub trait AsBoxDef: Send + Sync + 'static { - fn call(slf: Box>, net: &mut Net, port: Port); -} - -impl AsDef for BoxDef { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { - T::call(Box::from_raw(slf as *mut _), net, port) - } -} - -#[repr(transparent)] -pub struct ArcDef(pub T, PhantomData<()>); - -impl ArcDef { - pub fn new_arc(labs: LabSet, data: T) -> Arc> { - Arc::new(Def::new(labs, ArcDef(data, PhantomData))) - } - pub fn new_arc_port(labs: LabSet, data: T) -> Port { - unsafe { Port::new_ref(Arc::into_raw(Arc::new(Def::new(labs, ArcDef(data, PhantomData)))).as_ref().unwrap()) } - } - pub fn to_port(slf: Arc>) -> Port { - unsafe { Port::new_ref(Arc::into_raw(slf).as_ref().unwrap()) } - } - /// SAFETY: if port is a ref, it must be a valid pointer. - pub unsafe fn try_downcast_arc(port: Port) -> Option>> { - if port.is(Tag::Ref) && port != Port::ERA { - unsafe { Def::downcast_ptr::(port.addr().0 as *const _) } - .map(|port| unsafe { Arc::from_raw(port as *mut Def) }) - } else { - None - } - } -} - -pub trait AsArcDef: Send + Sync + 'static { - fn call(slf: Arc>, net: &mut Net, port: Port); -} - -impl AsDef for ArcDef { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { - T::call(Arc::from_raw(slf as *mut _), net, port); - } -} - -#[repr(transparent)] -pub struct HostedDef(pub T, PhantomData<()>); - -impl HostedDef { - pub unsafe fn new(labs: LabSet) -> DefRef - where - T: Default, - { - Self::new_hosted(labs, T::default()) - } - - pub unsafe fn new_hosted(labs: LabSet, data: T) -> DefRef { - DefRef::Owned(Box::new(Def::new(labs, HostedDef(data, PhantomData)))) - } -} - -pub trait AsHostedDef: Send + Sync + 'static { - fn call(slf: &Def, net: &mut Net, port: Port); -} - -impl AsDef for HostedDef { - unsafe fn call(slf: *const Def, net: &mut Net, port: Port) { - T::call((slf as *const Def).as_ref().unwrap(), net, port) - } -} - -#[derive(Copy, Clone)] -pub struct UniqueTreePtr(*mut Tree); -unsafe impl Send for UniqueTreePtr {} -unsafe impl Sync for UniqueTreePtr {} - -impl UniqueTreePtr { - unsafe fn to_box(self) -> Box { - Box::from_raw(self.0) - } -} - -pub struct ReadbackDef { - root: Arc, - host: Arc>, - var_idx: Arc, - tree: UniqueTreePtr, -} - -impl ReadbackDef { - fn maybe_finish(net: &mut Net, root: Arc) { - let Some(root) = Arc::into_inner(root) else { return }; - (root)(net) - } - fn with(&self, tree: *mut Tree) -> Port { - Port::new_ref(Box::leak(BoxDef::new_boxed(LabSet::ALL, Self { - tree: UniqueTreePtr(tree), - root: self.root.clone(), - var_idx: self.var_idx.clone(), - host: self.host.clone(), - }))) - } -} - -impl AsBoxDef for ReadbackDef { - fn call(def: Box>, net: &mut Net, port: Port) { - match port.tag() { - Tag::Var | Tag::Red => { - unreachable!() - } - Tag::Ref if port != Port::ERA => { - if let Some(other) = unsafe { BoxDef::::try_downcast_box(port.clone()) } { - let var = def.data.var_idx.fetch_add(1, Ordering::AcqRel); - let var = Tree::Var { nam: create_var(var) }; - unsafe { - (*def.data.tree.0) = var.clone(); - (*other.data.0.tree.0) = var; - } - Self::maybe_finish(net, other.data.0.root); - } else if let Some(back) = def.data.host.lock().back.get(&port.addr()) { - unsafe { *(def.data.tree.0) = Tree::Ref { nam: back.clone() } }; - } else { - unsafe { *(def.data.tree.0) = Tree::Era }; - } - } - Tag::Ref => { - unsafe { *(def.data.tree.0) = Tree::Era }; - } - Tag::Int => { - unsafe { *(def.data.tree.0) = Tree::Int { val: port.int() } }; - } - Tag::F32 => { - unsafe { *(def.data.tree.0) = Tree::F32 { val: port.float().into() } }; - } - Tag::Mat => { - unsafe { - *(def.data.tree.0) = - Tree::Mat { zero: Box::new(Tree::Era), succ: Box::new(Tree::Era), out: Box::new(Tree::Era) } - }; - let Tree::Mat { zero, succ, out } = (unsafe { &mut *(def.data.tree.0) }) else { unreachable!() }; - let old = port.clone().consume_node(); - let old_sel = old.p1.load_target().consume_node(); - 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 @ (Tag::Op | Tag::Ctr) => { - let old = port.clone().consume_node(); - let (lhs, rhs): (*mut Tree, *mut Tree) = match tag { - Tag::Op => { - unsafe { - *(def.data.tree.0) = Tree::Op { op: port.op(), rhs: Box::new(Tree::Era), out: Box::new(Tree::Era) } - }; - let Tree::Op { rhs, out, .. } = (unsafe { &mut *(def.data.tree.0) }) else { unreachable!() }; - (rhs.as_mut(), out.as_mut()) - } - Tag::Ctr => { - unsafe { *(def.data.tree.0) = Tree::Ctr { lab: port.lab(), ports: vec![Tree::Era, Tree::Era] } }; - let Tree::Ctr { ports, .. } = (unsafe { &mut *(def.data.tree.0) }) else { unreachable!() }; - (&mut ports[0], &mut ports[1]) - } - _ => unreachable!(), - }; - net.link_wire_port(old.p1, def.data.with(lhs)); - net.link_wire_port(old.p2, def.data.with(rhs)); - } - } - Self::maybe_finish(net, def.data.root); - } -} - -pub fn readback( - net: &mut Net, - host: Arc>, - from: Trg, - f: impl FnOnce(&mut Net, Tree) + Send + Sync + 'static, -) { - let root = UniqueTreePtr(Box::leak(Box::default())); - - let closure: Box = Box::new(move |net| { - let root = unsafe { root.to_box() }; - f(net, *root); - }); - - 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)), - }))), - ); -} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 9b96d848..c8cc6661 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -7,13 +7,12 @@ edition = "2021" path = "src/runtime.rs" [dependencies] -parking_lot = "0.12.2" - hvm64-util = { path = "../util", default-features = false } [features] +default = ["std"] std = ["hvm64-util/std"] -trace = [] +trace = ["std"] [lints] workspace = true diff --git a/runtime/src/def.rs b/runtime/src/def.rs index 939714d3..0849ac05 100644 --- a/runtime/src/def.rs +++ b/runtime/src/def.rs @@ -92,6 +92,8 @@ pub struct Def { pub data: T, } +pub type DynDef = dyn DerefMut + Send + Sync; + extern "C" { /// An internal type used to mark dynamic `Def`s. /// diff --git a/runtime/src/trace.rs b/runtime/src/trace.rs index 46937412..0d41f2c8 100644 --- a/runtime/src/trace.rs +++ b/runtime/src/trace.rs @@ -70,7 +70,8 @@ use core::{ sync::atomic::{AtomicBool, AtomicU64, Ordering}, }; -use parking_lot::{Mutex, Once}; +#[cfg(feature = "std")] +use std::sync::{Mutex, Once}; use crate::prelude::*; use hvm64_util::ops::TypedOp as Op; @@ -180,6 +181,7 @@ impl TraceData { } #[allow(clippy::vec_box)] // the address of `TraceLock` needs to remain stable +#[cfg(feature = "std")] static ACTIVE_TRACERS: Mutex>> = Mutex::new(Vec::new()); struct TraceWriter { @@ -196,8 +198,8 @@ impl Default for TraceWriter { data: UnsafeCell::new(TraceData { tid: 0, cursor: 0, data: Box::new([0; TRACE_SIZE]) }), }); let lock = unsafe { &*(&*boxed as *const _) }; - let mut active_tracers = ACTIVE_TRACERS.lock(); - active_tracers.push(boxed); + #[cfg(feature = "std")] + ACTIVE_TRACERS.lock().unwrap().push(boxed); TraceWriter { lock, nonce: TRACE_NONCE.fetch_add(1, Ordering::Relaxed) } } } @@ -294,7 +296,7 @@ impl<'a> TraceReader<'a> { #[cfg_attr(feature = "trace", no_mangle)] #[cfg(feature = "std")] pub fn _read_traces(limit: usize) { - let active_tracers = &*ACTIVE_TRACERS.lock(); + let active_tracers = &*ACTIVE_TRACERS.lock().unwrap(); let mut readers = active_tracers .iter() .enumerate() @@ -315,8 +317,9 @@ pub fn _read_traces(limit: usize) { eprintln!("{}", out); } +#[cfg(feature = "std")] pub unsafe fn _reset_traces() { - ACTIVE_TRACERS.lock().clear(); + ACTIVE_TRACERS.lock().unwrap().clear(); TRACE_NONCE.store(1, Ordering::Relaxed); } @@ -461,10 +464,10 @@ impl fmt::Debug for FmtWord { } } +#[cfg(feature = "std")] pub fn set_hook() { static ONCE: Once = Once::new(); if cfg!(feature = "trace") { - #[cfg(feature = "std")] ONCE.call_once(|| { use std::panic; let hook = panic::take_hook(); diff --git a/src/compile.rs b/src/compile.rs index b71515a4..39ddb23e 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -51,7 +51,6 @@ fn _compile_host(host: &Host) -> Result { extern crate alloc; use hvm64_runtime::{{*, ops::{{TypedOp, Ty::*, Op::*}}}}; -use core::ops::DerefMut; use alloc::boxed::Box; #[no_mangle] @@ -65,7 +64,7 @@ pub fn hvm64_dylib_v0__rust_version() -> &'static str {{ }} #[no_mangle] -pub fn hvm64_dylib_v0__insert_into(insert: &mut dyn FnMut(&str, Box + Send + Sync>)) {{ +pub fn hvm64_dylib_v0__insert_into(insert: &mut dyn FnMut(&str, Box)) {{ " )?; diff --git a/src/compile/include_files.rs b/src/compile/include_files.rs index fa70c973..d4d83f64 100644 --- a/src/compile/include_files.rs +++ b/src/compile/include_files.rs @@ -1,9 +1,7 @@ -use std::{fs, io, path::Path, sync::Arc}; +use std::{fs, io, path::Path}; use hvm64_host::Host; -use parking_lot::Mutex; - macro_rules! include_files { ($([$($prefix:ident)*])? crate $name:ident {$($sub:tt)*} $($rest:tt)*) => { include_files!([$($($prefix)*)?] $name/ { Cargo.toml src/ { $($sub)* } }); @@ -38,8 +36,8 @@ macro_rules! include_files { /// Copies the `hvm-64` source to a temporary `.hvm` directory. /// Only a subset of `Cargo.toml` is included. -pub fn create_temp_hvm(host: Arc>) -> Result<(), io::Error> { - let lib = super::compile_host(&host.lock()); +pub fn create_temp_hvm(host: &Host) -> Result<(), io::Error> { + let lib = super::compile_host(host); let outdir = ".hvm"; if Path::new(&outdir).exists() { fs::remove_dir_all(outdir)?; diff --git a/src/main.rs b/src/main.rs index eecacd59..cd7c30e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,26 +9,19 @@ use std::{ env::consts::{DLL_PREFIX, DLL_SUFFIX}, ffi::OsStr, fs, io, - ops::DerefMut, path::PathBuf, process::{self, Stdio}, - sync::Arc, time::{Duration, Instant}, }; -use parking_lot::Mutex; - use self::full::{CliMode, FullCli}; use args::{RunArgs, RuntimeOpts, TransformArgs, TransformPass}; use clap::Parser; use hvm64_ast::{Book, Net, Tree}; -use hvm64_host::{ - stdlib::{create_host, insert_stdlib}, - DefRef, Host, -}; -use hvm64_runtime::{Def, Heap, Port, Trg}; +use hvm64_host::Host; +use hvm64_runtime::{DynDef, Heap, Port, Trg}; use hvm64_transform::Transform; fn main() { @@ -50,8 +43,8 @@ fn main() { process::exit(1); }; - let host = create_host(&load_book(&[file], transform_args)); - compile::create_temp_hvm(host).unwrap(); + let host = Host::new(&load_book(&[file], transform_args)); + compile::create_temp_hvm(&host).unwrap(); compile_temp_hvm().unwrap(); @@ -63,12 +56,12 @@ fn main() { transform_args.transform_opts.prune_entrypoints.push(args.entry_point.clone()); let host = load_host(&[file], transform_args, &run_opts.include); - run(host, run_opts, args); + run(&host, run_opts, args); } CliMode::Reduce { run_opts, transform_args, files, exprs } => { let host = load_host(&files, transform_args, &run_opts.include); let exprs: Vec<_> = exprs.iter().map(|x| x.parse().unwrap()).collect(); - reduce_exprs(host, &exprs, &run_opts); + reduce_exprs(&host, &exprs, &run_opts); } CliMode::Transform { transform_args, files } => { let book = load_book(&files, transform_args); @@ -81,7 +74,7 @@ fn main() { } } -fn run(host: Arc>, opts: RuntimeOpts, args: RunArgs) { +fn run(host: &Host, opts: RuntimeOpts, args: RunArgs) { let mut net = Net { root: Tree::Ref { nam: args.entry_point }, redexes: vec![] }; for arg in args.args { let arg: Net = arg.parse().unwrap(); @@ -92,15 +85,10 @@ fn run(host: Arc>, opts: RuntimeOpts, args: RunArgs) { reduce_exprs(host, &[net], &opts); } -fn load_host( - files: &[PathBuf], - transform_args: TransformArgs, - include: &[PathBuf], -) -> Arc> { - let host: Arc> = Default::default(); - load_dylibs(host.clone(), include); - insert_stdlib(host.clone()); - host.lock().insert_book(&load_book(files, transform_args)); +fn load_host(files: &[PathBuf], transform_args: TransformArgs, include: &[PathBuf]) -> Host { + let mut host: Host = Default::default(); + load_dylibs(&mut host, include); + host.insert_book(&load_book(files, transform_args)); host } @@ -135,7 +123,7 @@ fn load_book(files: &[PathBuf], transform_args: TransformArgs) -> Book { book } -fn load_dylibs(host: Arc>, include: &[PathBuf]) { +fn load_dylibs(host: &mut Host, include: &[PathBuf]) { let current_dir = std::env::current_dir().unwrap(); for file in include { @@ -168,11 +156,10 @@ fn load_dylibs(host: Arc>, include: &[PathBuf]) { } let insert_into = lib - .get:: + Send + Sync>))>(b"hvm64_dylib_v0__insert_into") + .get::))>(b"hvm64_dylib_v0__insert_into") .expect("failed to load insert_into"); - let mut host = host.lock(); insert_into(&mut |name, def| { - host.insert_def(name, DefRef::Owned(def)); + host.insert_def(name, def); }); // Leak the lib to avoid unloading it, as code from it is still referenced. @@ -181,11 +168,11 @@ fn load_dylibs(host: Arc>, include: &[PathBuf]) { } } -fn reduce_exprs(host: Arc>, exprs: &[Net], opts: &RuntimeOpts) { +fn reduce_exprs(host: &Host, exprs: &[Net], opts: &RuntimeOpts) { let heap = Heap::new(opts.memory).expect("memory allocation failed"); for expr in exprs { let net = &mut hvm64_runtime::Net::new(&heap); - host.lock().encode_net(net, Trg::port(Port::new_var(net.root.addr())), expr); + host.encode_net(net, Trg::port(Port::new_var(net.root.addr())), expr); let start_time = Instant::now(); if opts.single_core { net.normal(); @@ -193,7 +180,7 @@ fn reduce_exprs(host: Arc>, exprs: &[Net], opts: &RuntimeOpts) { net.parallel_normal(); } let elapsed = start_time.elapsed(); - println!("{}", host.lock().readback(net)); + println!("{}", host.readback(net)); if opts.show_stats { print_stats(net, elapsed); } diff --git a/tests/cli.rs b/tests/cli.rs index 7e06e36b..58cdacb3 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -160,20 +160,6 @@ fn test_cli_transform() { @sub = (<- a b> (a b)) "### ); - - // Test log - - assert_snapshot!( - execute_hvm64(&[ - "transform", - "-Opre-reduce", - &(env!("CARGO_MANIFEST_DIR").to_owned() + "/tests/programs/log.hvm") - ]).unwrap().1, - @r###" - @main = a - & @HVM.log ~ (#1 (#2 a)) - "### - ); } #[test] diff --git a/tests/loaders.rs b/tests/loaders.rs index 533c17ce..0787a97a 100644 --- a/tests/loaders.rs +++ b/tests/loaders.rs @@ -2,7 +2,7 @@ // use hvm64::{ast::*, run, stdlib::create_host}; use hvm64_ast::{Book, Net}; -use hvm64_host::stdlib::create_host; +use hvm64_host::Host; use hvm64_runtime as run; use std::fs; @@ -27,13 +27,13 @@ pub fn replace_template(mut code: String, map: &[(&str, &str)]) -> String { pub fn normal_with(book: Book, mem: Option, entry_point: &str) -> (run::Rewrites, Net) { let area = run::Heap::new(mem).unwrap(); - let host = create_host(&book); + let host = Host::new(&book); let mut rnet = run::Net::new(&area); - rnet.boot(&host.lock().defs[entry_point]); + rnet.boot(&host.defs[entry_point]); rnet.normal(); - let net = host.lock().readback(&rnet); + let net = host.readback(&rnet); (rnet.rwts, net) } diff --git a/tests/programs/heavy_pre_reduction.bend b/tests/programs/heavy_pre_reduction.bend index dd612e8b..c3bc34f3 100644 --- a/tests/programs/heavy_pre_reduction.bend +++ b/tests/programs/heavy_pre_reduction.bend @@ -11,11 +11,13 @@ fib = expensive = (fib (C4 C2)) -erase = λn λx (n HVM.black_box x) -black_plus = (HVM.black_box plus) +erase = λn λx (n black_box x) +black_plus = (black_box plus) expensive_1 = (fib (C4 C2)) expensive_2 = (fib (C4 C2)) main_fast = (black_plus expensive expensive ) main_slow = (black_plus expensive_1 expensive_2 ) -main = * \ No newline at end of file +main = * + +black_box = @x x diff --git a/tests/programs/heavy_pre_reduction.hvm b/tests/programs/heavy_pre_reduction.hvm index 41c42cb3..c0e5624c 100644 --- a/tests/programs/heavy_pre_reduction.hvm +++ b/tests/programs/heavy_pre_reduction.hvm @@ -4,8 +4,8 @@ @C4 = ({7 (a b) {7 (c a) {7 (d c) (e d)}}} (e b)) @C6 = ({5 (a b) {5 (c a) {5 (d c) {5 (e d) {5 (f e) (g f)}}}}} (g b)) @black_plus = a -& @HVM.black_box ~ (@plus a) -@erase = ((@HVM.black_box (a b)) (a b)) +& @black_box ~ (@plus a) +@erase = ((@black_box (a b)) (a b)) @expensive = a & @fib ~ (b a) & @C4 ~ (@C2 b) @@ -24,4 +24,5 @@ @main_slow = a & @black_plus ~ (@expensive_1 (@expensive_2 a)) @plus = ((a (b c)) ((d (e b)) ({11 a d} (e c)))) +@black_box = (x x) diff --git a/tests/programs/log.bend b/tests/programs/log.bend deleted file mode 100644 index ef813b5d..00000000 --- a/tests/programs/log.bend +++ /dev/null @@ -1 +0,0 @@ -main = (HVM.log 1 2) \ No newline at end of file diff --git a/tests/programs/log.hvm b/tests/programs/log.hvm deleted file mode 100644 index 49d90ca4..00000000 --- a/tests/programs/log.hvm +++ /dev/null @@ -1,3 +0,0 @@ -@main = a -& @HVM.log ~ (#1 (#2 a)) - diff --git a/tests/snapshots/tests__pre_reduce_run@heavy_pre_reduction.hvm.snap b/tests/snapshots/tests__pre_reduce_run@heavy_pre_reduction.hvm.snap index a8e509b4..74502467 100644 --- a/tests/snapshots/tests__pre_reduce_run@heavy_pre_reduction.hvm.snap +++ b/tests/snapshots/tests__pre_reduce_run@heavy_pre_reduction.hvm.snap @@ -5,11 +5,11 @@ input_file: tests/programs/heavy_pre_reduction.hvm --- * pre-reduce: -RWTS : 53_147 -- ANNI : 13_124 +RWTS : 53_165 +- ANNI : 13_137 - COMM : 35_175 - ERAS : 4_794 -- DREF : 54 +- DREF : 59 - OPER : 0 run: RWTS : 2 diff --git a/tests/snapshots/tests__pre_reduce_run@log.hvm.snap b/tests/snapshots/tests__pre_reduce_run@log.hvm.snap deleted file mode 100644 index 7a55463b..00000000 --- a/tests/snapshots/tests__pre_reduce_run@log.hvm.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: tests/tests.rs -expression: output -input_file: tests/programs/log.hvm ---- -#2 -pre-reduce: -RWTS : 0 -- ANNI : 0 -- COMM : 0 -- ERAS : 0 -- DREF : 0 -- OPER : 0 -run: -RWTS : 7 -- ANNI : 2 -- COMM : 0 -- ERAS : 0 -- DREF : 5 -- OPER : 0 diff --git a/tests/snapshots/tests__run@log.hvm.snap b/tests/snapshots/tests__run@log.hvm.snap deleted file mode 100644 index fc4fda14..00000000 --- a/tests/snapshots/tests__run@log.hvm.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: tests/tests.rs -expression: output -input_file: tests/programs/log.hvm ---- -#2 -RWTS : 7 -- ANNI : 2 -- COMM : 0 -- ERAS : 0 -- DREF : 5 -- OPER : 0 diff --git a/tests/tests.rs b/tests/tests.rs index 26446e0c..3312f9e4 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,43 +1,37 @@ #![cfg(feature = "std")] use hvm64_transform::pre_reduce::PreReduce; -use parking_lot::Mutex; use std::{ fs, io::{self, Write}, path::{Path, PathBuf}, str::FromStr, - sync::Arc, time::Instant, }; use hvm64_ast::{self as ast, Book, Net}; -use hvm64_host::{stdlib::create_host, Host}; +use hvm64_host::Host; use hvm64_runtime as run; use insta::assert_snapshot; use serial_test::serial; -fn execute_host(host: Arc>) -> Option<(run::Rewrites, Net)> { +fn execute_host(host: &Host) -> Option<(run::Rewrites, Net)> { let heap = run::Heap::new(None).unwrap(); let mut net = run::Net::new(&heap); - // The host is locked inside this block. - { - let lock = host.lock(); - let Some(entrypoint) = lock.defs.get("main") else { - println!(" skipping"); - return None; - }; - net.boot(entrypoint); - } + let Some(entrypoint) = host.defs.get("main") else { + println!(" skipping"); + return None; + }; + net.boot(entrypoint); let start = Instant::now(); net.parallel_normal(); println!(" {:.3?}", start.elapsed()); - Some((net.rwts, host.lock().readback(&net))) + Some((net.rwts, host.readback(&net))) } -fn test_run(name: &str, host: Arc>) { +fn test_run(name: &str, host: &Host) { print!("{name}..."); io::stdout().flush().unwrap(); @@ -57,8 +51,8 @@ fn test_pre_reduce_run(path: &str, mut book: Book) { print!(" {:.3?}...", start.elapsed()); io::stdout().flush().unwrap(); - let host = create_host(&book); - let Some((rwts, net)) = execute_host(host) else { + let host = Host::new(&book); + let Some((rwts, net)) = execute_host(&host) else { assert_snapshot!(&pre_stats.rewrites); return; }; @@ -70,13 +64,13 @@ fn test_pre_reduce_run(path: &str, mut book: Book) { fn test_path(path: &Path) { let code = fs::read_to_string(path).unwrap(); let book = ast::Book::from_str(&code).unwrap(); - let host = create_host(&book); + let host = Host::new(&book); let path = path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap(); let path = path.to_str().unwrap(); test_pre_reduce_run(path, book.clone()); - test_run(path, host); + test_run(path, &host); } fn test_dir(dir: &Path, filter: impl Fn(&Path) -> bool) { diff --git a/tests/transform.rs b/tests/transform.rs index b9eaae5e..89af340d 100644 --- a/tests/transform.rs +++ b/tests/transform.rs @@ -17,13 +17,17 @@ use loaders::*; #[test] /// Test that ensures that pre_reduce only reduces repeated refs once. pub fn test_fast_pre_reduce() { - let book = parse_core(&load_file("heavy_pre_reduction.hvm")); + let mut book = parse_core(&load_file("heavy_pre_reduction.hvm")); + let black_box = book.remove("black_box").unwrap(); let (mut book_1, mut book_2) = (book.clone(), book); let rwts_1 = book_1.pre_reduce(&|x| !["expensive", "main_fast"].contains(&x), None, u64::MAX).rewrites; let rwts_2 = book_2.pre_reduce(&|x| !["expensive_1", "expensive_2", "main_slow"].contains(&x), None, u64::MAX).rewrites; + book_1.insert("black_box".to_owned(), black_box.clone()); + book_2.insert("black_box".to_owned(), black_box); + let rwts_1 = rwts_1 + normal_with(book_1, None, "main_fast").0; let rwts_2 = rwts_2 + normal_with(book_2, None, "main_slow").0; diff --git a/transform/Cargo.toml b/transform/Cargo.toml index 801ece67..14bc7a50 100644 --- a/transform/Cargo.toml +++ b/transform/Cargo.toml @@ -8,7 +8,6 @@ path = "src/transform.rs" [dependencies] ordered-float = { version = "4.2.0", default-features = false } -parking_lot = "0.12.2" thiserror = "1.0.58" hvm64-ast = { path = "../ast", default-features = false } diff --git a/transform/src/pre_reduce.rs b/transform/src/pre_reduce.rs index fd5f1885..1ee1f87d 100644 --- a/transform/src/pre_reduce.rs +++ b/transform/src/pre_reduce.rs @@ -16,15 +16,12 @@ use crate::prelude::*; use hvm64_ast::{Book, Tree}; -use hvm64_host::{ - stdlib::{AsHostedDef, HostedDef}, - DefRef, Host, -}; -use hvm64_runtime::{Def, Heap, InterpretedDef, LabSet, Port, Rewrites}; +use hvm64_host::Host; +use hvm64_runtime::{AsDef, Def, Heap, InterpretedDef, LabSet, Port, Rewrites}; use hvm64_util::maybe_grow; -use alloc::sync::Arc; -use parking_lot::Mutex; +use alloc::rc::Rc; +use core::cell::Cell; pub trait PreReduce { fn pre_reduce(&mut self, skip: &dyn Fn(&str) -> bool, max_memory: Option, max_rwts: u64) -> PreReduceStats; @@ -39,11 +36,9 @@ impl PreReduce for Book { /// `max_memory` is measured in bytes. fn pre_reduce(&mut self, skip: &dyn Fn(&str) -> bool, max_memory: Option, max_rwts: u64) -> PreReduceStats { let mut host = Host::default(); - let captured_redexes = Arc::new(Mutex::new(Vec::new())); + let captured_redexes = Rc::new(Cell::new(Vec::new())); // When a ref is not found in the `Host`, put an inert def in its place. - host.insert_book_with_default(self, &mut |_| unsafe { - HostedDef::new_hosted(LabSet::ALL, InertDef(captured_redexes.clone())) - }); + host.insert_book_with_default(self, &mut |_| Box::new(Def::new(LabSet::ALL, InertDef(captured_redexes.clone())))); let area = Heap::new(max_memory).expect("pre-reduce memory allocation failed"); let mut state = State { @@ -90,11 +85,18 @@ enum SeenState { /// A Def that pushes all interactions to its inner Vec. #[derive(Default)] -struct InertDef(Arc>>); - -impl AsHostedDef for InertDef { - fn call(def: &Def, _: &mut hvm64_runtime::Net, port: Port) { - def.data.0.lock().push((Port::new_ref(def), port)); +struct InertDef(Rc>>); + +// Safety: we don't actually send/share this across threads +unsafe impl Send for InertDef {} +unsafe impl Sync for InertDef {} + +impl AsDef for InertDef { + unsafe fn call(def: *const Def, _: &mut hvm64_runtime::Net, port: Port) { + let def = unsafe { &*def }; + let mut vec = def.data.0.take(); + vec.push((Port::new_ref(def), port)); + def.data.0.set(vec); } } @@ -106,7 +108,7 @@ struct State<'a> { max_rwts: u64, area: &'a Heap, - captured_redexes: Arc>>, + captured_redexes: Rc>>, skip: &'a dyn Fn(&str) -> bool, seen: Map, @@ -146,16 +148,15 @@ impl<'a> State<'a> { self.rewrites += rt.rwts; // Move interactions with inert defs back into the net redexes array - self.captured_redexes.lock().drain(..).for_each(|r| rt.redux(r.0, r.1)); + let mut captured_redexes = self.captured_redexes.take(); + captured_redexes.drain(..).for_each(|r| rt.redux(r.0, r.1)); + self.captured_redexes.set(captured_redexes); let net = self.host.readback(&rt); // Mutate the host in-place with the pre-reduced net. let instr = self.host.encode_def(&net); - if let DefRef::Owned(def_box) = self.host.defs.get_mut(nam).unwrap() { - let interpreted_def: &mut Def = def_box.downcast_mut().unwrap(); - interpreted_def.data = instr; - }; + self.host.get_mut::(nam).data = instr; // Replace the "Cycled" state with the "Reduced" state *self.seen.get_mut(nam).unwrap() = SeenState::Reduced { net, normal: n_reduced.is_some() }; From 0b400d4617a5be5f2f399a0ae2268ec7e3cac187 Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 14:07:50 -0400 Subject: [PATCH 4/8] streamline deps --- Cargo.lock | 9 ---- ast/Cargo.toml | 6 +-- ast/src/ast.rs | 84 +++++++++++++++++------------------- host/Cargo.toml | 5 ++- prelude.rs | 1 - runtime/Cargo.toml | 4 +- src/compile/include_files.rs | 2 +- transform/Cargo.toml | 6 +-- util/Cargo.toml | 6 +-- util/src/array_vec.rs | 29 ------------- util/src/lib.rs | 21 ++++----- util/src/multi_iterator.rs | 40 +++++++++++++++++ 12 files changed, 101 insertions(+), 112 deletions(-) delete mode 100644 util/src/array_vec.rs create mode 100644 util/src/multi_iterator.rs diff --git a/Cargo.lock b/Cargo.lock index 0b88971e..f2335691 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,12 +69,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "autocfg" version = "1.2.0" @@ -297,10 +291,8 @@ name = "hvm64-ast" version = "0.3.0" dependencies = [ "TSPL", - "arrayvec", "hvm64-util", "ordered-float", - "thiserror", ] [[package]] @@ -335,7 +327,6 @@ dependencies = [ name = "hvm64-util" version = "0.3.0" dependencies = [ - "arrayvec", "stacker", ] diff --git a/ast/Cargo.toml b/ast/Cargo.toml index f59262ae..b2894996 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -7,16 +7,14 @@ edition = "2021" path = "src/ast.rs" [dependencies] -arrayvec = { version = "0.7.4", default-features = false } ordered-float = { version = "4.2.0", default-features = false } -thiserror = { version = "1.0.59", optional = true } TSPL = { version = "0.0.12", optional = true } -hvm64-util = { path = "../util", default-features = false } +hvm64-util = { path = "../util" } [features] default = ["std", "parser"] -std = ["hvm64-util/std", "ordered-float/std", "dep:thiserror"] +std = [] parser = ["dep:TSPL"] [lints] diff --git a/ast/src/ast.rs b/ast/src/ast.rs index 52bef8cd..81e9491a 100644 --- a/ast/src/ast.rs +++ b/ast/src/ast.rs @@ -9,9 +9,6 @@ //! The AST is based on the [interaction calculus]. //! //! [interaction calculus]: https://en.wikipedia.org/wiki/Interaction_nets#Interaction_calculus -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] include!("../../prelude.rs"); @@ -21,9 +18,8 @@ mod parser; use alloc::collections::BTreeMap; use crate::prelude::*; -use hvm64_util::{array_vec, create_var, deref, maybe_grow, ops::TypedOp as Op, var_to_num}; +use hvm64_util::{create_var, deref, maybe_grow, multi_iterator, ops::TypedOp as Op, var_to_num}; -use arrayvec::ArrayVec; use ordered_float::OrderedFloat; pub type Lab = u16; @@ -194,28 +190,26 @@ impl Net { impl Tree { #[inline(always)] pub fn children(&self) -> impl ExactSizeIterator + DoubleEndedIterator { - ArrayVec::<_, MAX_ARITY>::into_iter(match self { - Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => { - array_vec::from_array([]) - } - Tree::Ctr { ports, .. } => array_vec::from_iter(ports), - Tree::Op { rhs, out, .. } => array_vec::from_array([rhs, out]), - Tree::Mat { zero, succ, out } => array_vec::from_array([zero, succ, out]), - Tree::Adt { fields, .. } => array_vec::from_iter(fields), - }) + multi_iterator! { Iter { Nil, Two, Three, Vec } } + match self { + Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => Iter::Nil([]), + Tree::Ctr { ports, .. } => Iter::Vec(ports), + Tree::Op { rhs, out, .. } => Iter::Two([&**rhs, out]), + Tree::Mat { zero, succ, out } => Iter::Three([&**zero, succ, out]), + Tree::Adt { fields, .. } => Iter::Vec(fields), + } } #[inline(always)] pub fn children_mut(&mut self) -> impl ExactSizeIterator + DoubleEndedIterator { - ArrayVec::<_, MAX_ARITY>::into_iter(match self { - Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => { - array_vec::from_array([]) - } - Tree::Ctr { ports, .. } => array_vec::from_iter(ports), - Tree::Op { rhs, out, .. } => array_vec::from_array([rhs, out]), - Tree::Mat { zero, succ, out } => array_vec::from_array([zero, succ, out]), - Tree::Adt { fields, .. } => array_vec::from_iter(fields), - }) + multi_iterator! { Iter { Nil, Two, Three, Vec } } + match self { + Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => Iter::Nil([]), + Tree::Ctr { ports, .. } => Iter::Vec(ports), + Tree::Op { rhs, out, .. } => Iter::Two([&mut **rhs, out]), + Tree::Mat { zero, succ, out } => Iter::Three([&mut **zero, succ, out]), + Tree::Adt { fields, .. } => Iter::Vec(fields), + } } pub fn lab(&self) -> Option { @@ -373,24 +367,26 @@ impl fmt::Display for Tree { } } -// #[test] -// fn test_tree_drop() { -// use alloc::vec; - -// drop(Tree::from_str("((* (* *)) (* *))")); - -// let mut long_tree = Tree::Era; -// let mut cursor = &mut long_tree; -// for _ in 0 .. 100_000 { -// *cursor = Tree::Ctr { lab: 0, ports: vec![Tree::Era, Tree::Era] }; -// let Tree::Ctr { ports, .. } = cursor else { unreachable!() }; -// cursor = &mut ports[0]; -// } -// drop(long_tree); - -// let mut big_tree = Tree::Era; -// for _ in 0 .. 16 { -// big_tree = Tree::Ctr { lab: 0, ports: vec![big_tree.clone(), big_tree] }; -// } -// drop(big_tree); -// } +#[test] +#[cfg(feature = "parser")] +fn test_tree_drop() { + use alloc::vec; + use core::str::FromStr; + + drop(Tree::from_str("((* (* *)) (* *))")); + + let mut long_tree = Tree::Era; + let mut cursor = &mut long_tree; + for _ in 0 .. 100_000 { + *cursor = Tree::Ctr { lab: 0, ports: vec![Tree::Era, Tree::Era] }; + let Tree::Ctr { ports, .. } = cursor else { unreachable!() }; + cursor = &mut ports[0]; + } + drop(long_tree); + + let mut big_tree = Tree::Era; + for _ in 0 .. 16 { + big_tree = Tree::Ctr { lab: 0, ports: vec![big_tree.clone(), big_tree] }; + } + drop(big_tree); +} diff --git a/host/Cargo.toml b/host/Cargo.toml index 2aebbca7..3a5b2901 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -7,12 +7,13 @@ edition = "2021" path = "src/host.rs" [dependencies] +hvm64-util = { path = "../util" } hvm64-ast = { path = "../ast", default-features = false } -hvm64-util = { path = "../util", default-features = false } hvm64-runtime = { path = "../runtime", default-features = false } [features] -std = ["hvm64-ast/std", "hvm64-util/std", "hvm64-runtime/std"] +default = ["std"] +std = ["hvm64-ast/std", "hvm64-runtime/std"] [lints] workspace = true diff --git a/prelude.rs b/prelude.rs index cf38a55a..0ae61b05 100644 --- a/prelude.rs +++ b/prelude.rs @@ -2,7 +2,6 @@ extern crate alloc; #[allow(unused)] mod prelude { - pub use alloc::{ borrow::ToOwned, boxed::Box, diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index c8cc6661..d05a9e7d 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -7,11 +7,11 @@ edition = "2021" path = "src/runtime.rs" [dependencies] -hvm64-util = { path = "../util", default-features = false } +hvm64-util = { path = "../util" } [features] default = ["std"] -std = ["hvm64-util/std"] +std = [] trace = ["std"] [lints] diff --git a/src/compile/include_files.rs b/src/compile/include_files.rs index d4d83f64..43942a50 100644 --- a/src/compile/include_files.rs +++ b/src/compile/include_files.rs @@ -76,11 +76,11 @@ hvm64-runtime = { path = "../runtime", default-features = false } prelude crate util { lib - array_vec bi_enum create_var deref maybe_grow + multi_iterator ops { num word diff --git a/transform/Cargo.toml b/transform/Cargo.toml index 14bc7a50..3ccccf36 100644 --- a/transform/Cargo.toml +++ b/transform/Cargo.toml @@ -10,14 +10,14 @@ path = "src/transform.rs" ordered-float = { version = "4.2.0", default-features = false } thiserror = "1.0.58" -hvm64-ast = { path = "../ast", default-features = false } +hvm64-util = { path = "../util" } hvm64-runtime = { path = "../runtime", default-features = false } -hvm64-util = { path = "../util", default-features = false } +hvm64-ast = { path = "../ast", default-features = false } hvm64-host = { path = "../host", default-features = false } [features] default = ["std"] -std = ["hvm64-ast/std", "hvm64-runtime/std", "hvm64-util/std", "hvm64-host/std"] +std = ["hvm64-runtime/std", "hvm64-ast/std", "hvm64-host/std"] [lints] workspace = true diff --git a/util/Cargo.toml b/util/Cargo.toml index 0d5fabea..304adf21 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -4,11 +4,7 @@ version = "0.3.0" edition = "2021" [dependencies] -arrayvec = { version = "0.7.4", default-features = false } -stacker = { version = "0.1.15", default-features = false } - -[features] -std = ["arrayvec/std"] +stacker = { version = "0.1.15" } [lints] workspace = true diff --git a/util/src/array_vec.rs b/util/src/array_vec.rs deleted file mode 100644 index 3e2c6b32..00000000 --- a/util/src/array_vec.rs +++ /dev/null @@ -1,29 +0,0 @@ -use arrayvec::ArrayVec; - -struct Assert; - -trait IsTrue {} - -impl IsTrue for Assert {} - -#[allow(private_bounds)] -pub fn from_array(array: [T; LEN]) -> ArrayVec -where - Assert<{ LEN <= CAP }>: IsTrue, -{ - let mut vec = ArrayVec::new(); - unsafe { - for el in array { - vec.push_unchecked(el) - } - } - vec -} - -pub fn from_iter(iter: impl IntoIterator) -> ArrayVec { - let mut vec = ArrayVec::new(); - for item in iter { - vec.push(item); - } - vec -} diff --git a/util/src/lib.rs b/util/src/lib.rs index 5f066894..be7b857e 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -1,19 +1,16 @@ -#![cfg_attr(not(feature = "std"), no_std)] -#![allow(incomplete_features)] -#![feature(generic_const_exprs)] - include!("../../prelude.rs"); -pub mod array_vec; -pub mod bi_enum; -pub mod create_var; -pub mod deref; -pub mod maybe_grow; pub mod ops; -pub mod parse_abbrev_number; -pub mod pretty_num; -pub use array_vec::*; +mod bi_enum; +mod deref; +mod multi_iterator; + +mod create_var; +mod maybe_grow; +mod parse_abbrev_number; +mod pretty_num; + pub use create_var::*; pub use maybe_grow::*; pub use parse_abbrev_number::*; diff --git a/util/src/multi_iterator.rs b/util/src/multi_iterator.rs new file mode 100644 index 00000000..1a660b51 --- /dev/null +++ b/util/src/multi_iterator.rs @@ -0,0 +1,40 @@ +/// A macro for creating iterators that can have statically known +/// different types. Useful for iterating over tree children, where +/// each tree node variant yields a different iterator type. +#[macro_export] +macro_rules! multi_iterator { + ($Iter:ident { $($Variant:ident),* $(,)? }) => { + #[derive(Debug, Clone)] + enum $Iter<$($Variant),*> { + $($Variant { iter: $Variant }),* + } + + impl<$($Variant),*> $Iter<$($Variant),*> { + $( + #[allow(non_snake_case)] + fn $Variant(iter: impl IntoIterator) -> Self { + $Iter::$Variant { iter: iter.into_iter() } + } + )* + } + + impl),*> Iterator for $Iter<$($Variant),*> { + type Item = T; + fn next(&mut self) -> Option { + match self { $($Iter::$Variant { iter } => iter.next()),* } + } + + fn size_hint(&self) -> (usize, Option) { + match self { $($Iter::$Variant { iter } => iter.size_hint()),* } + } + } + + impl),*> DoubleEndedIterator for $Iter<$($Variant),*> { + fn next_back(&mut self) -> Option { + match self { $($Iter::$Variant { iter } => iter.next_back()),* } + } + } + + impl),*> ExactSizeIterator for $Iter<$($Variant),*> {} + }; +} From 374f6fc08e2ef2d9b881044645ddb8c1ccaa23d1 Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 15:47:36 -0400 Subject: [PATCH 5/8] remove n-ary nodes from ast --- ast/src/ast.rs | 152 +++++-------------------- ast/src/parser.rs | 72 +++--------- examples/sort/radix/radix_sort_ctr.hvm | 4 +- host/src/encode.rs | 42 +------ host/src/readback.rs | 4 +- src/args.rs | 2 - tests/transform.rs | 64 ++--------- transform/src/coalesce_ctrs.rs | 29 ----- transform/src/encode_adts.rs | 81 ------------- transform/src/eta_reduce.rs | 58 ++++------ transform/src/transform.rs | 25 +--- 11 files changed, 84 insertions(+), 449 deletions(-) delete mode 100644 transform/src/coalesce_ctrs.rs delete mode 100644 transform/src/encode_adts.rs diff --git a/ast/src/ast.rs b/ast/src/ast.rs index 81e9491a..33d2f942 100644 --- a/ast/src/ast.rs +++ b/ast/src/ast.rs @@ -72,16 +72,8 @@ pub enum Tree { /// The label of the combinator. (Combinators with the same label /// annihilate, and combinators with different labels commute.) lab: Lab, - /// The auxiliary ports of this node. - /// - /// - 0 ports: this behaves identically to an eraser node. - /// - 1 port: this behaves identically to a wire. - /// - 2 ports: this is a standard binary combinator node. - /// - 3+ ports: equivalent to right-chained binary nodes; `(a b c)` is - /// equivalent to `(a (b c))`. - /// - /// The length of this vector must be less than [`MAX_ARITY`]. - ports: Vec, + lft: Box, + rgt: Box, }, /// A binary node representing an operation on native integers. /// @@ -98,59 +90,18 @@ pub enum Tree { /// /// The principal port connects to the integer to be matched on. Mat { - /// An auxiliary port; connects to the zero branch. - zero: Box, - /// An auxiliary port; connects to the a CTR with label 0 containing the - /// predecessor and the output of the succ branch. - succ: Box, + /// An auxiliary port; connects to a tree of the following structure: + /// ```text + /// (+value_if_zero (-predecessor_of_number +value_if_succ)) + /// ``` + arms: Box, /// An auxiliary port; connects to the output. out: Box, }, - /// An Scott-encoded ADT node. - /// - /// This is always equivalent to: - /// ```text - /// {$lab - /// * * * ... // one era node per `variant_index` - /// {$lab $f0 $f1 $f2 ... R} // each field, in order, followed by a var node - /// * * * ... // one era node per `variant_count - variant_index - 1` - /// R // a var node - /// } - /// ``` - /// - /// For example: - /// ```text - /// data Option = None | (Some value): - /// None: - /// (0:2) = Adt { lab: 0, variant_index: 0, variant_count: 2, fields: [] } - /// (R * R) = Ctr { lab: 0, ports: [Var { nam: "R" }, Era, Var { nam: "R" }]} - /// (Some 123): - /// (1:2 #123) = Adt { lab: 0, variant_index: 0, variant_count: 2, fields: [Int { val: 123 }] } - /// (* (#123 R) R) = Ctr { lab: 0, ports: [Era, Ctr { lab: 0, ports: [Int { val: 123 }, Var { nam: "R" }] }, Var { nam: "R" }]} - /// ``` - Adt { - lab: Lab, - /// The index of the variant of this ADT node. - /// - /// Must be less than `variant_count`. - variant_index: usize, - /// The number of variants in the data type. - /// - /// Must be greater than `0` and less than `MAX_ADT_VARIANTS`. - variant_count: usize, - /// The fields of this ADT node. - /// - /// Must have a length less than `MAX_ADT_FIELDS`. - fields: Vec, - }, /// One side of a wire; the other side will have the same name. Var { nam: String }, } -pub const MAX_ARITY: usize = 8; -pub const MAX_ADT_VARIANTS: usize = MAX_ARITY - 1; -pub const MAX_ADT_FIELDS: usize = MAX_ARITY - 1; - impl Net { pub fn trees(&self) -> impl Iterator { iter::once(&self.root).chain(self.redexes.iter().flat_map(|(x, y)| [x, y])) @@ -173,7 +124,7 @@ impl Net { let fresh_str = create_var(fresh + 1); let fun = mem::take(&mut self.root); - let app = Tree::Ctr { lab: 0, ports: vec![arg, Tree::Var { nam: fresh_str.clone() }] }; + let app = Tree::Ctr { lab: 0, lft: Box::new(arg), rgt: Box::new(Tree::Var { nam: fresh_str.clone() }) }; self.root = Tree::Var { nam: fresh_str }; self.redexes.push((fun, app)); } @@ -190,45 +141,33 @@ impl Net { impl Tree { #[inline(always)] pub fn children(&self) -> impl ExactSizeIterator + DoubleEndedIterator { - multi_iterator! { Iter { Nil, Two, Three, Vec } } + multi_iterator! { Iter { Nil, Two } } match self { Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => Iter::Nil([]), - Tree::Ctr { ports, .. } => Iter::Vec(ports), + Tree::Ctr { lft, rgt, .. } => Iter::Two([&**lft, rgt]), Tree::Op { rhs, out, .. } => Iter::Two([&**rhs, out]), - Tree::Mat { zero, succ, out } => Iter::Three([&**zero, succ, out]), - Tree::Adt { fields, .. } => Iter::Vec(fields), + Tree::Mat { arms, out } => Iter::Two([&**arms, out]), } } #[inline(always)] pub fn children_mut(&mut self) -> impl ExactSizeIterator + DoubleEndedIterator { - multi_iterator! { Iter { Nil, Two, Three, Vec } } + multi_iterator! { Iter { Nil, Two } } match self { Tree::Era | Tree::Int { .. } | Tree::F32 { .. } | Tree::Ref { .. } | Tree::Var { .. } => Iter::Nil([]), - Tree::Ctr { ports, .. } => Iter::Vec(ports), + Tree::Ctr { lft, rgt, .. } => Iter::Two([&mut **lft, rgt]), Tree::Op { rhs, out, .. } => Iter::Two([&mut **rhs, out]), - Tree::Mat { zero, succ, out } => Iter::Three([&mut **zero, succ, out]), - Tree::Adt { fields, .. } => Iter::Vec(fields), + Tree::Mat { arms, out } => Iter::Two([&mut **arms, out]), } } pub fn lab(&self) -> Option { match self { - Tree::Ctr { lab, ports } if ports.len() >= 2 => Some(*lab), - Tree::Adt { lab, .. } => Some(*lab), + Tree::Ctr { lab, .. } => Some(*lab), _ => None, } } - pub fn legacy_mat(mut arms: Tree, out: Tree) -> Option { - let Tree::Ctr { lab: 0, ports } = &mut arms else { None? }; - let ports = mem::take(ports); - let Ok([zero, succ]) = <[_; 2]>::try_from(ports) else { None? }; - let zero = Box::new(zero); - let succ = Box::new(succ); - Some(Tree::Mat { zero, succ, out: Box::new(out) }) - } - /// Increases `fresh` until `create_var(*fresh)` does not conflict /// with a [`Tree::Var`] in `tree` /// @@ -252,12 +191,9 @@ impl Clone for Tree { Tree::Int { val } => Tree::Int { val: *val }, Tree::F32 { val } => Tree::F32 { val: *val }, Tree::Ref { nam } => Tree::Ref { nam: nam.clone() }, - Tree::Ctr { lab, ports } => Tree::Ctr { lab: *lab, ports: ports.clone() }, + Tree::Ctr { lab, lft, rgt } => Tree::Ctr { lab: *lab, lft: lft.clone(), rgt: rgt.clone() }, Tree::Op { op, rhs, out } => Tree::Op { op: *op, rhs: rhs.clone(), out: out.clone() }, - Tree::Mat { zero, succ, out } => Tree::Mat { zero: zero.clone(), succ: succ.clone(), out: out.clone() }, - Tree::Adt { lab, variant_index, variant_count, fields } => { - Tree::Adt { lab: *lab, variant_index: *variant_index, variant_count: *variant_count, fields: fields.clone() } - } + Tree::Mat { arms, out } => Tree::Mat { arms: arms.clone(), out: out.clone() }, Tree::Var { nam } => Tree::Var { nam: nam.clone() }, }) } @@ -319,50 +255,17 @@ impl fmt::Display for Tree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { maybe_grow(move || match self { Tree::Era => write!(f, "*"), - Tree::Ctr { lab, ports } => { - match lab { - 0 => write!(f, "("), - 1 => write!(f, "["), - _ => write!(f, "{{{lab}"), - }?; - let mut space = *lab > 1; - for port in ports { - if space { - write!(f, " ")?; - } - write!(f, "{port}")?; - space = true; - } - match lab { - 0 => write!(f, ")"), - 1 => write!(f, "]"), - _ => write!(f, "}}"), - }?; - Ok(()) - } - Tree::Adt { lab, variant_index, variant_count, fields } => { - match lab { - 0 => write!(f, "("), - 1 => write!(f, "["), - _ => write!(f, "{{{lab}"), - }?; - write!(f, ":{}:{}", variant_index, variant_count)?; - for field in fields { - write!(f, " {field}")?; - } - match lab { - 0 => write!(f, ")"), - 1 => write!(f, "]"), - _ => write!(f, "}}"), - }?; - Ok(()) - } + Tree::Ctr { lab, lft, rgt } => match lab { + 0 => write!(f, "({lft} {rgt})"), + 1 => write!(f, "[{lft} {rgt}]"), + _ => write!(f, "{{{lab} {lft} {rgt}}}"), + }, Tree::Var { nam } => write!(f, "{nam}"), Tree::Ref { nam } => write!(f, "@{nam}"), Tree::Int { val } => write!(f, "#{val}"), Tree::F32 { val } => write!(f, "#{:?}", val.0), Tree::Op { op, rhs, out } => write!(f, "<{op} {rhs} {out}>"), - Tree::Mat { zero, succ, out } => write!(f, "?<{zero} {succ} {out}>"), + Tree::Mat { arms, out } => write!(f, "?<{arms} {out}>"), }) } } @@ -370,7 +273,6 @@ impl fmt::Display for Tree { #[test] #[cfg(feature = "parser")] fn test_tree_drop() { - use alloc::vec; use core::str::FromStr; drop(Tree::from_str("((* (* *)) (* *))")); @@ -378,15 +280,15 @@ fn test_tree_drop() { let mut long_tree = Tree::Era; let mut cursor = &mut long_tree; for _ in 0 .. 100_000 { - *cursor = Tree::Ctr { lab: 0, ports: vec![Tree::Era, Tree::Era] }; - let Tree::Ctr { ports, .. } = cursor else { unreachable!() }; - cursor = &mut ports[0]; + *cursor = Tree::Ctr { lab: 0, lft: Box::new(Tree::Era), rgt: Box::new(Tree::Era) }; + let Tree::Ctr { lft, .. } = cursor else { unreachable!() }; + cursor = lft; } drop(long_tree); let mut big_tree = Tree::Era; for _ in 0 .. 16 { - big_tree = Tree::Ctr { lab: 0, ports: vec![big_tree.clone(), big_tree] }; + big_tree = Tree::Ctr { lab: 0, lft: Box::new(big_tree.clone()), rgt: Box::new(big_tree) }; } drop(big_tree); } diff --git a/ast/src/parser.rs b/ast/src/parser.rs index 6d0d6792..ac25ee35 100644 --- a/ast/src/parser.rs +++ b/ast/src/parser.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use alloc::collections::BTreeMap; use core::str::FromStr; -use crate::{Book, Lab, Net, Tree, MAX_ADT_FIELDS, MAX_ADT_VARIANTS, MAX_ARITY}; +use crate::{Book, Lab, Net, Tree}; use hvm64_util::{maybe_grow, ops::TypedOp as Op}; use TSPL::{new_parser, Parser}; @@ -57,52 +57,16 @@ impl<'i> Hvm64Parser<'i> { _ => unreachable!(), }; let close = match char { - '(' => ')', - '[' => ']', - '{' => '}', + '(' => ")", + '[' => "]", + '{' => "}", _ => unreachable!(), }; self.skip_trivia(); - if self.peek_one().is_some_and(|x| x == ':') { - self.advance_one(); - let variant_index = self.parse_u64()?; - self.consume(":")?; - let variant_count = self.parse_u64()?; - let mut fields = Vec::new(); - self.skip_trivia(); - while self.peek_one() != Some(close) { - fields.push(self.parse_tree()?); - self.skip_trivia(); - } - self.advance_one(); - if variant_count == 0 { - Err("variant count cannot be zero".to_owned())?; - } - if variant_count > (MAX_ADT_VARIANTS as u64) { - Err("adt has too many variants".to_owned())?; - } - if variant_index >= variant_count { - Err("variant index out of range".to_owned())?; - } - let variant_index = variant_index as usize; - let variant_count = variant_count as usize; - if fields.len() > MAX_ADT_FIELDS { - Err("adt has too many fields".to_owned())?; - } - Ok(Tree::Adt { lab, variant_index, variant_count, fields }) - } else { - let mut ports = Vec::new(); - self.skip_trivia(); - while self.peek_one() != Some(close) { - ports.push(self.parse_tree()?); - self.skip_trivia(); - } - self.advance_one(); - if ports.len() > MAX_ARITY { - Err("ctr has too many ports".to_owned())?; - } - Ok(Tree::Ctr { lab, ports }) - } + let lft = Box::new(self.parse_tree()?); + let rgt = Box::new(self.parse_tree()?); + self.consume(close)?; + Ok(Tree::Ctr { lab, lft, rgt }) } // Ref = "@" Name Some('@') => { @@ -143,21 +107,11 @@ impl<'i> Hvm64Parser<'i> { } // Mat = "?<" Tree Tree ">" Some('?') => { - self.advance_one(); - self.consume("<")?; - let zero = self.parse_tree()?; - let succ = self.parse_tree()?; - self.skip_trivia(); - if self.peek_one() == Some('>') { - self.advance_one(); - Tree::legacy_mat(zero, succ).ok_or_else(|| "invalid legacy match".to_owned()) - } else { - let zero = Box::new(zero); - let succ = Box::new(succ); - let out = Box::new(self.parse_tree()?); - self.consume(">")?; - Ok(Tree::Mat { zero, succ, out }) - } + self.consume("?<")?; + let arms = Box::new(self.parse_tree()?); + let out = Box::new(self.parse_tree()?); + self.consume(">")?; + Ok(Tree::Mat { arms, out }) } // Var = Name _ => Ok(Tree::Var { nam: self.parse_name()? }), diff --git a/examples/sort/radix/radix_sort_ctr.hvm b/examples/sort/radix/radix_sort_ctr.hvm index 05bf998d..036fe31b 100644 --- a/examples/sort/radix/radix_sort_ctr.hvm +++ b/examples/sort/radix/radix_sort_ctr.hvm @@ -5,7 +5,7 @@ @Single = (a (* ((a b) (* b)))) @Used = (* @Used$C0) @Used$C0 = (a (* a)) -@gen = (?<@gen$C0 @gen$C1 a> a) +@gen = (?<(@gen$C0 @gen$C1) a> a) @gen$C0 = (a b) & @Single ~ (a b) @gen$C1 = ({7 a b} (<<< #1 {9 c <| #1 d>}> e)) @@ -69,7 +69,7 @@ @sum$C0 = (a (b c)) & @sum ~ (a <+ d c>) & @sum ~ (b d) -@swap = (?<@swap$C0 @swap$C2 a> a) +@swap = (?<(@swap$C0 @swap$C2) a> a) @swap$C0 = (a (b c)) & @Node ~ (a (b c)) @swap$C1 = (a (b c)) diff --git a/host/src/encode.rs b/host/src/encode.rs index b98b07d2..06878a0d 100644 --- a/host/src/encode.rs +++ b/host/src/encode.rs @@ -65,43 +65,15 @@ impl<'a, E: Encoder> State<'a, E> { self.visit_tree(tree, trg); } fn visit_tree(&mut self, tree: &'a Tree, trg: E::Trg) { - static ERA: Tree = Tree::Era; maybe_grow(move || match tree { Tree::Era => self.encoder.link_const(trg, Port::ERA), Tree::Int { val } => self.encoder.link_const(trg, Port::new_int(*val)), Tree::F32 { val } => self.encoder.link_const(trg, Port::new_float(val.0)), Tree::Ref { nam } => self.encoder.link_const(trg, Port::new_ref(&self.host.defs[nam])), - Tree::Ctr { lab, ports } => { - if ports.is_empty() { - return self.visit_tree(&ERA, trg); - } - let mut trg = trg; - for port in &ports[0 .. ports.len() - 1] { - let (l, r) = self.encoder.ctr(*lab, trg); - self.visit_tree(port, l); - trg = r; - } - self.visit_tree(ports.last().unwrap(), trg); - } - Tree::Adt { lab, variant_index, variant_count, fields } => { - let mut trg = trg; - for _ in 0 .. *variant_index { - let (l, r) = self.encoder.ctr(*lab, trg); - self.visit_tree(&ERA, l); - trg = r; - } - let (mut l, mut r) = self.encoder.ctr(*lab, trg); - for field in fields { - let (x, y) = self.encoder.ctr(*lab, l); - self.visit_tree(field, x); - l = y; - } - for _ in 0 .. (*variant_count - *variant_index - 1) { - let (x, y) = self.encoder.ctr(*lab, r); - self.visit_tree(&ERA, x); - r = y; - } - self.encoder.link(l, r); + Tree::Ctr { lab, lft, rgt } => { + let (l, r) = self.encoder.ctr(*lab, trg); + self.visit_tree(lft, l); + self.visit_tree(rgt, r); } Tree::Op { op, rhs: lft, out: rgt } => match &**lft { Tree::Int { val } => { @@ -118,11 +90,9 @@ impl<'a, E: Encoder> State<'a, E> { self.visit_tree(rgt, r); } }, - Tree::Mat { zero, succ, out } => { + Tree::Mat { arms, out } => { let (a, o) = self.encoder.mat(trg); - let (z, s) = self.encoder.ctr(0, a); - self.visit_tree(zero, z); - self.visit_tree(succ, s); + self.visit_tree(arms, a); self.visit_tree(out, o); } Tree::Var { nam } => match self.scope.entry(nam) { diff --git a/host/src/readback.rs b/host/src/readback.rs index eb9753a6..478da9cc 100644 --- a/host/src/readback.rs +++ b/host/src/readback.rs @@ -72,13 +72,13 @@ impl<'a> ReadbackState<'a> { } Tag::Ctr => { let node = port.traverse_node(); - Tree::Ctr { lab: node.lab, ports: vec![self.read_wire(node.p1), self.read_wire(node.p2)] } + Tree::Ctr { lab: node.lab, lft: Box::new(self.read_wire(node.p1)), rgt: Box::new(self.read_wire(node.p2)) } } Tag::Mat => { let node = port.traverse_node(); let arms = self.read_wire(node.p1); let out = self.read_wire(node.p2); - Tree::legacy_mat(arms, out).expect("invalid mat node") + Tree::Mat { arms: Box::new(arms), out: Box::new(out) } } }) } diff --git a/src/args.rs b/src/args.rs index f7c2bd14..4709f3f1 100644 --- a/src/args.rs +++ b/src/args.rs @@ -127,8 +127,6 @@ macro_rules! transform_passes { transform_passes! { pre_reduce: "pre-reduce" | "pre", - coalesce_ctrs: "coalesce-ctrs" | "coalesce", - encode_adts: "encode-adts" | "adts", eta_reduce: "eta-reduce" | "eta", inline: "inline", prune: "prune", diff --git a/tests/transform.rs b/tests/transform.rs index 89af340d..30794c90 100644 --- a/tests/transform.rs +++ b/tests/transform.rs @@ -2,11 +2,8 @@ //! Tests for transformation passes -use hvm64_ast::{Book, Net, Tree}; -use hvm64_transform::{ - coalesce_ctrs::CoalesceCtrs, encode_adts::EncodeAdts, eta_reduce::EtaReduce, inline::Inline, pre_reduce::PreReduce, - prune::Prune, TransformError, -}; +use hvm64_ast::{Book, Net}; +use hvm64_transform::{eta_reduce::EtaReduce, inline::Inline, pre_reduce::PreReduce, prune::Prune, TransformError}; use insta::assert_snapshot; use std::str::FromStr; @@ -49,34 +46,6 @@ pub fn test_fast_pre_reduce() { "###) } -#[test] -pub fn test_adt_encoding() { - pub fn parse_and_encode(net: &str) -> String { - let mut net = Net::from_str(net).unwrap(); - net.trees_mut().for_each(Tree::coalesce_constructors); - net.trees_mut().for_each(Tree::encode_scott_adts); - format!("{net}") - } - assert_snapshot!(parse_and_encode("(a (b (c d)))"), @"(a b c d)"); - assert_snapshot!(parse_and_encode("(a (b c (d e)))"), @"(a b c d e)"); - assert_snapshot!(parse_and_encode("(a b c d e f g h)"), @"(a b c d e f g h)"); - assert_snapshot!(parse_and_encode("(a b c d (e f g h (i j k l)))"), @"(a b c d (e f g h (i j k l)))"); - - assert_snapshot!(parse_and_encode("(* ((a R) R))"), @"(:1:2 a)"); - assert_snapshot!(parse_and_encode("((a R) (* R))"), @"(:0:2 a)"); - assert_snapshot!(parse_and_encode("(* (* ((a R) R)))"), @"(:2:3 a)"); - assert_snapshot!(parse_and_encode("(* ((a R) (* R)))"), @"(:1:3 a)"); - assert_snapshot!(parse_and_encode("((a (b R)) R)"), @"(:0:1 a b)"); - assert_snapshot!(parse_and_encode("((a (b (c R))) R)"), @"(:0:1 a b c)"); - assert_snapshot!(parse_and_encode("(* ((a (b (c R))) R))"), @"(:1:2 a b c)"); - assert_snapshot!(parse_and_encode("{4 * {4 {4 a {4 b {4 c R}}} R}}"), @"{4:1:2 a b c}"); - assert_snapshot!(parse_and_encode("(* x x)"), @"(:1:2)"); - assert_snapshot!(parse_and_encode("(((((* x x) x) * x) x) * x)"), @"(:0:2 (:0:2 (:1:2)))"); - assert_snapshot!(parse_and_encode("(a b * (a b c) * c)"), @"(a b (:1:3 a b))"); - assert_snapshot!(parse_and_encode("(* (:0:1))"), @"(:1:2)"); - assert_snapshot!(parse_and_encode("(a * (:0:1))"), @"(a (:1:2))"); -} - #[test] pub fn test_eta() { pub fn parse_and_reduce(net: &str) -> String { @@ -85,14 +54,14 @@ pub fn test_eta() { format!("{net}") } assert_snapshot!(parse_and_reduce("((x y) (x y))"), @"(x x)"); - assert_snapshot!(parse_and_reduce("((a b c d e f) (a b c d e f))"), @"(a a)"); + assert_snapshot!(parse_and_reduce("((a (b (c (d (e f))))) (a (b (c (d (e f))))))"), @"(a a)"); assert_snapshot!(parse_and_reduce("<+ (a b) (a b)>"), @"<+ a a>"); assert_snapshot!(parse_and_reduce("(a b) & ((a b) (c d)) ~ (c d) "), @r###" a & (a c) ~ c "###); assert_snapshot!(parse_and_reduce("((a b) [a b])"), @"((a b) [a b])"); - assert_snapshot!(parse_and_reduce("((a b c) b c)"), @"((a b) b)"); + assert_snapshot!(parse_and_reduce("((a (b c)) (b c))"), @"((a b) b)"); assert_snapshot!(parse_and_reduce("([(a b) (c d)] [(a b) (c d)])"), @"(a a)"); assert_snapshot!(parse_and_reduce("(* *)"), @"*"); assert_snapshot!(parse_and_reduce("([(#0 #0) (#12345 #12345)] [(* *) (a a)])"), @"([#0 #12345] [* (a a)])"); @@ -107,25 +76,16 @@ pub fn test_inline() { assert_snapshot!(parse_and_inline(" @era = * @num = #123 - @abab = (a b a b) + @abab = (a (b (a b))) @ref = @abab @def = @ref @eff = @def - @I = (:0:1) - @K = (:0:2) - @1234 = (:1:2 (:3:4)) @foo = @bar @bar = @baz @baz = @unbound - @into = ((@era @num @abab @ref @def @eff @I @K) (@into @1234 @foo @bar @baz @unbound)) + @into = (@era (@num (@abab (@ref (@def (@eff (@into (@foo (@bar (@baz @unbound)))))))))) ").unwrap(), @r###" - @1234 = (:1:2 (:3:4)) - - @I = (:0:1) - - @K = (:0:2) - - @abab = (a b a b) + @abab = (a (b (a b))) @bar = @unbound @@ -139,7 +99,7 @@ pub fn test_inline() { @foo = @unbound - @into = ((* #123 @abab @abab @abab @abab (:0:1) (:0:2)) (@into @1234 @unbound @unbound @unbound @unbound)) + @into = (* (#123 (@abab (@abab (@abab (@abab (@into (@unbound (@unbound (@unbound @unbound)))))))))) @num = #123 @@ -160,14 +120,14 @@ pub fn test_prune() { } assert_snapshot!(parse_and_prune(" @self = (* @self) - @main = (@main @a @b) - @a = (@b @c @d) + @main = (@main (@a @b)) + @a = (@b (@c @d)) @b = (@c @c) @c = @d @d = @at @idk = (@e @f) "), @r###" - @a = (@b @c @d) + @a = (@b (@c @d)) @b = (@c @c) @@ -175,6 +135,6 @@ pub fn test_prune() { @d = @at - @main = (@main @a @b) + @main = (@main (@a @b)) "###); } diff --git a/transform/src/coalesce_ctrs.rs b/transform/src/coalesce_ctrs.rs deleted file mode 100644 index 4d3451fc..00000000 --- a/transform/src/coalesce_ctrs.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::prelude::*; -use hvm64_ast::{Tree, MAX_ARITY}; -use hvm64_util::maybe_grow; - -pub trait CoalesceCtrs { - fn coalesce_constructors(&mut self); -} - -impl CoalesceCtrs for Tree { - /// Join chains of CTR nodes, such as `(a (b (c d)))` into n-ary nodes `(a b c - /// d)` - fn coalesce_constructors(&mut self) { - maybe_grow(|| match self { - Tree::Ctr { lab, ports } => { - ports.iter_mut().for_each(Tree::coalesce_constructors); - match &mut ports.pop() { - Some(Tree::Ctr { lab: inner_lab, ports: inner_ports }) - if inner_lab == lab && ports.len() + inner_ports.len() < MAX_ARITY => - { - ports.append(inner_ports); - } - Some(other) => ports.push(mem::take(other)), - None => (), - } - } - other => other.children_mut().for_each(Tree::coalesce_constructors), - }) - } -} diff --git a/transform/src/encode_adts.rs b/transform/src/encode_adts.rs deleted file mode 100644 index 21ebcb2d..00000000 --- a/transform/src/encode_adts.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::prelude::*; -use hvm64_ast::{Tree, MAX_ADT_VARIANTS}; -use hvm64_util::maybe_grow; - -pub trait EncodeAdts { - fn encode_scott_adts(&mut self); -} - -impl EncodeAdts for Tree { - /// Encode scott-encoded ADTs into optimized compact ADT nodes - fn encode_scott_adts(&mut self) { - maybe_grow(|| match self { - &mut Tree::Ctr { lab, ref mut ports } => { - fn get_adt_info(lab: u16, ports: &[Tree]) -> Option<(usize, usize)> { - let Some(Tree::Var { nam: ret_var }) = ports.last() else { return None }; - - let mut variant_index = None; - for (idx, i) in ports.iter().enumerate().rev().skip(1) { - match i { - Tree::Era => {} - Tree::Ctr { lab: inner_lab, ports } if *inner_lab == lab && variant_index.is_none() => { - // Ensure that the last port is the return variable - let Some(Tree::Var { nam }) = ports.last() else { return None }; - if nam != ret_var { - return None; - } - variant_index = Some(idx); - } - // Nilary variant - Tree::Var { nam } if nam == ret_var && variant_index.is_none() => { - variant_index = Some(idx); - } - // Does not encode an ADT. - _ => return Some((idx + 1, variant_index?)), - } - } - Some((0, variant_index?)) - } - - if let Some((start, idx)) = get_adt_info(lab, ports) { - let fields = match &mut ports.swap_remove(idx) { - Tree::Ctr { ports: fields, .. } => { - fields.pop(); - mem::take(fields) - } - Tree::Var { .. } => vec![], - _ => unreachable!(), - }; - let adt = Tree::Adt { lab, variant_index: idx - start, variant_count: ports.len() - start, fields }; - if start == 0 { - *self = adt; - } else { - ports.truncate(start); - ports.push(adt); - } - } else { - while let Some(&Tree::Adt { lab: inner_lab, variant_count, .. }) = ports.last() { - if inner_lab == lab - && variant_count < MAX_ADT_VARIANTS - && matches!(ports.get(ports.len() - 2), Some(&Tree::Era)) - { - ports.swap_remove(ports.len() - 2); - let Some(Tree::Adt { variant_index, variant_count, .. }) = ports.last_mut() else { unreachable!() }; - *variant_index += 1; - *variant_count += 1; - if ports.len() == 1 { - *self = ports.pop().unwrap(); - break; - } - } else { - break; - } - } - } - - self.children_mut().for_each(Tree::encode_scott_adts); - } - other => other.children_mut().for_each(Tree::encode_scott_adts), - }) - } -} diff --git a/transform/src/eta_reduce.rs b/transform/src/eta_reduce.rs index 71a6e12e..b12a0598 100644 --- a/transform/src/eta_reduce.rs +++ b/transform/src/eta_reduce.rs @@ -98,14 +98,10 @@ struct Phase1<'a> { impl<'a> Phase1<'a> { fn walk_tree(&mut self, tree: &'a Tree) { match tree { - Tree::Ctr { lab, ports } => { - let last_port = ports.len() - 1; - for (idx, i) in ports.iter().enumerate() { - if idx != last_port { - self.nodes.push(NodeType::Ctr(*lab)); - } - self.walk_tree(i); - } + Tree::Ctr { lab, lft, rgt } => { + self.nodes.push(NodeType::Ctr(*lab)); + self.walk_tree(lft); + self.walk_tree(rgt); } Tree::Var { nam } => { if let Some(i) = self.vars.get(&**nam) { @@ -136,42 +132,28 @@ struct Phase2 { } impl Phase2 { - fn reduce_ctr(&mut self, lab: u16, ports: &mut Vec, skip: usize) -> NodeType { - if skip == ports.len() { - return NodeType::Other; - } - if skip == ports.len() - 1 { - return self.reduce_tree(&mut ports[skip]); - } - let head_index = self.index.next().unwrap(); - let a = self.reduce_tree(&mut ports[skip]); - let b = self.reduce_ctr(lab, ports, skip + 1); - if a == b { - let reducible = match a { - NodeType::Var(delta) => self.nodes[head_index.wrapping_add_signed(delta)] == NodeType::Ctr(lab), - NodeType::Era | NodeType::Int(_) | NodeType::F32(_) => true, - _ => false, - }; - if reducible { - ports.pop(); - return a; - } - } - NodeType::Ctr(lab) - } fn reduce_tree(&mut self, tree: &mut Tree) -> NodeType { - if let Tree::Ctr { lab, ports } = tree { - let ty = self.reduce_ctr(*lab, ports, 0); - if ports.len() == 1 { - *tree = ports.pop().unwrap(); + let index = self.index.next().unwrap(); + let ty = self.nodes[index]; + if let Tree::Ctr { lft, rgt, .. } = tree { + let a = self.reduce_tree(lft); + let b = self.reduce_tree(rgt); + if a == b { + let reducible = match a { + NodeType::Var(delta) => self.nodes[index.wrapping_add_signed(delta)] == ty, + NodeType::Era | NodeType::Int(_) | NodeType::F32(_) => true, + _ => false, + }; + if reducible { + *tree = mem::take(lft); + return a; + } } - ty } else { - let index = self.index.next().unwrap(); for i in tree.children_mut() { self.reduce_tree(i); } - self.nodes[index] } + ty } } diff --git a/transform/src/transform.rs b/transform/src/transform.rs index 2054b9ba..4ef6f811 100644 --- a/transform/src/transform.rs +++ b/transform/src/transform.rs @@ -4,15 +4,11 @@ include!("../../prelude.rs"); use hvm64_ast::Book; -pub mod coalesce_ctrs; -pub mod encode_adts; pub mod eta_reduce; pub mod inline; pub mod pre_reduce; pub mod prune; -use coalesce_ctrs::CoalesceCtrs; -use encode_adts::EncodeAdts; use eta_reduce::EtaReduce; use inline::Inline; use pre_reduce::PreReduce; @@ -51,14 +47,6 @@ impl Transform for Book { if passes.eta_reduce { def.eta_reduce(); } - for tree in def.trees_mut() { - if passes.coalesce_ctrs { - tree.coalesce_constructors(); - } - if passes.encode_adts { - tree.encode_scott_adts(); - } - } } if passes.inline { loop { @@ -66,19 +54,12 @@ impl Transform for Book { if inline_changed.is_empty() { break; } - if !(passes.eta_reduce || passes.encode_adts) { + if !passes.eta_reduce { break; } for name in inline_changed { let def = self.get_mut(&name).unwrap(); - if passes.eta_reduce { - def.eta_reduce(); - } - if passes.encode_adts { - for tree in def.trees_mut() { - tree.encode_scott_adts(); - } - } + def.eta_reduce(); } } } @@ -121,8 +102,6 @@ macro_rules! transform_passes { transform_passes! { pre_reduce, - coalesce_ctrs, - encode_adts, eta_reduce, inline, prune, From 25c8b89e5b1aa2aa45686c4906bb5988d26b9d0a Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 16:36:05 -0400 Subject: [PATCH 6/8] actually no_std --- .github/workflows/checks.yml | 2 ++ Cargo.lock | 7 ++----- Cargo.toml | 5 ++++- ast/Cargo.toml | 6 +++--- ast/src/ast.rs | 1 + host/Cargo.toml | 4 ++-- runtime/Cargo.toml | 4 ++-- runtime/src/runtime.rs | 1 + transform/Cargo.toml | 5 ++--- transform/src/transform.rs | 12 ++++++++++-- util/Cargo.toml | 6 +++++- util/src/lib.rs | 2 ++ util/src/maybe_grow.rs | 6 ++++++ 13 files changed, 42 insertions(+), 19 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 2717b200..8bdad5b6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -24,6 +24,8 @@ jobs: - run: RUSTFLAGS="-D warnings" cargo check --all-targets - run: RUSTFLAGS="-D warnings" cargo check --all-targets --features trace - run: RUSTFLAGS="-D warnings" cargo check --all-targets --no-default-features + - run: cargo install cargo-no-std-check + - run: cargo no-std-check --workspace --exclude hvm64 --no-default-features clippy: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/Cargo.lock b/Cargo.lock index f2335691..b1daa4d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,8 +5,7 @@ version = 3 [[package]] name = "TSPL" version = "0.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfd6238b1461b99635b26585a85b4e7b9c786cc0481b3c540ae5f590b6dfb6" +source = "git+https://github.com/tjjfvi/TSPL?branch=no_std#35e172d6369794c6a81c9cd1bb249277e66e79c6" dependencies = [ "highlight_error", ] @@ -266,8 +265,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "highlight_error" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e18805660d7b6b2e2b9f316a5099521b5998d5cba4dda11b5157a21aaef03" +source = "git+https://github.com/tjjfvi/rust_highlight_error/?branch=no_std#e4136b3dd5a80699ecbab8fe708433da21ff1be1" [[package]] name = "hvm64" @@ -320,7 +318,6 @@ dependencies = [ "hvm64-runtime", "hvm64-util", "ordered-float", - "thiserror", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c25e4188..ffeb91ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ debug = "full" [dependencies] clap = { version = "4.5.4", features = ["derive"] } libloading = { version = "0.8.3", default-features = false } -ordered-float = { version = "4.2.0" } +ordered-float = { version = "4.2.0", default-features = false } thiserror = "1.0.58" hvm64-ast = { path = "./ast" } @@ -52,3 +52,6 @@ serial_test = "3.0.0" default = ["std"] std = [] trace = ["hvm64-runtime/trace"] + +[patch.crates-io] +highlight_error = { git = "https://github.com/tjjfvi/rust_highlight_error/", branch = "no_std" } diff --git a/ast/Cargo.toml b/ast/Cargo.toml index b2894996..4a6d9a3f 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -8,13 +8,13 @@ path = "src/ast.rs" [dependencies] ordered-float = { version = "4.2.0", default-features = false } -TSPL = { version = "0.0.12", optional = true } +TSPL = { git = "https://github.com/tjjfvi/TSPL", branch = "no_std", optional = true } -hvm64-util = { path = "../util" } +hvm64-util = { path = "../util", default-features = false } [features] default = ["std", "parser"] -std = [] +std = ["hvm64-util/std"] parser = ["dep:TSPL"] [lints] diff --git a/ast/src/ast.rs b/ast/src/ast.rs index 33d2f942..2dee343d 100644 --- a/ast/src/ast.rs +++ b/ast/src/ast.rs @@ -9,6 +9,7 @@ //! The AST is based on the [interaction calculus]. //! //! [interaction calculus]: https://en.wikipedia.org/wiki/Interaction_nets#Interaction_calculus +#![cfg_attr(not(feature = "std"), no_std)] include!("../../prelude.rs"); diff --git a/host/Cargo.toml b/host/Cargo.toml index 3a5b2901..e337a6d5 100644 --- a/host/Cargo.toml +++ b/host/Cargo.toml @@ -7,13 +7,13 @@ edition = "2021" path = "src/host.rs" [dependencies] -hvm64-util = { path = "../util" } +hvm64-util = { path = "../util", default-features = false } hvm64-ast = { path = "../ast", default-features = false } hvm64-runtime = { path = "../runtime", default-features = false } [features] default = ["std"] -std = ["hvm64-ast/std", "hvm64-runtime/std"] +std = ["hvm64-util/std", "hvm64-ast/std", "hvm64-runtime/std"] [lints] workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index d05a9e7d..c8cc6661 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -7,11 +7,11 @@ edition = "2021" path = "src/runtime.rs" [dependencies] -hvm64-util = { path = "../util" } +hvm64-util = { path = "../util", default-features = false } [features] default = ["std"] -std = [] +std = ["hvm64-util/std"] trace = ["std"] [lints] diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index 7b35dcf7..9a14ee34 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -19,6 +19,7 @@ //! - active pairs are thus stored in a dedicated vector, `net.redexes` #![feature(const_type_id, extern_types, inline_const, new_uninit)] #![cfg_attr(feature = "trace", feature(const_type_name))] +#![cfg_attr(not(feature = "std"), no_std)] include!("../../prelude.rs"); diff --git a/transform/Cargo.toml b/transform/Cargo.toml index 3ccccf36..1024942e 100644 --- a/transform/Cargo.toml +++ b/transform/Cargo.toml @@ -8,16 +8,15 @@ path = "src/transform.rs" [dependencies] ordered-float = { version = "4.2.0", default-features = false } -thiserror = "1.0.58" -hvm64-util = { path = "../util" } +hvm64-util = { path = "../util", default-features = false } hvm64-runtime = { path = "../runtime", default-features = false } hvm64-ast = { path = "../ast", default-features = false } hvm64-host = { path = "../host", default-features = false } [features] default = ["std"] -std = ["hvm64-runtime/std", "hvm64-ast/std", "hvm64-host/std"] +std = ["hvm64-util/std", "hvm64-runtime/std", "hvm64-ast/std", "hvm64-host/std"] [lints] workspace = true diff --git a/transform/src/transform.rs b/transform/src/transform.rs index 4ef6f811..b6bfcc4f 100644 --- a/transform/src/transform.rs +++ b/transform/src/transform.rs @@ -2,6 +2,8 @@ include!("../../prelude.rs"); +use crate::prelude::*; + use hvm64_ast::Book; pub mod eta_reduce; @@ -16,12 +18,18 @@ use prune::Prune; #[derive(Debug, Clone, PartialEq, Eq)] #[non_exhaustive] -#[cfg_attr(feature = "std", derive(thiserror::Error))] pub enum TransformError { - #[cfg_attr(feature = "std", error("infinite reference cycle in `@{0}`"))] InfiniteRefCycle(String), } +impl fmt::Display for TransformError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TransformError::InfiniteRefCycle(name) => write!(f, "infinite reference cycle in `@{name}`"), + } + } +} + pub trait Transform { fn transform(&mut self, passes: TransformPasses, opts: &TransformOpts) -> Result<(), TransformError>; } diff --git a/util/Cargo.toml b/util/Cargo.toml index 304adf21..b2cfbf56 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -4,7 +4,11 @@ version = "0.3.0" edition = "2021" [dependencies] -stacker = { version = "0.1.15" } +stacker = { version = "0.1.15", optional = true } + +[features] +default = ["std"] +std = ["dep:stacker"] [lints] workspace = true diff --git a/util/src/lib.rs b/util/src/lib.rs index be7b857e..d080f897 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_std)] + include!("../../prelude.rs"); pub mod ops; diff --git a/util/src/maybe_grow.rs b/util/src/maybe_grow.rs index 11c04cf8..2025d57b 100644 --- a/util/src/maybe_grow.rs +++ b/util/src/maybe_grow.rs @@ -1,4 +1,10 @@ /// Guard against stack overflows in recursive functions. +#[cfg(feature = "std")] pub fn maybe_grow(f: impl FnOnce() -> R) -> R { stacker::maybe_grow(1024 * 32, 1024 * 1024, f) } + +#[cfg(not(feature = "std"))] +pub fn maybe_grow(f: impl FnOnce() -> R) -> R { + f() +} From dd3769cbdb7d5474efb4dfa9c544d778603dfaa0 Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 16:41:20 -0400 Subject: [PATCH 7/8] link to std in dylib so we can allocate --- src/compile.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compile.rs b/src/compile.rs index 39ddb23e..8d44f6a3 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -45,7 +45,6 @@ fn _compile_host(host: &Host) -> Result { write!( code, " -#![no_std] #![allow(warnings)] extern crate alloc; From 89993b65a96854b7c0507a0f053b40e24e476a50 Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 17:14:06 -0400 Subject: [PATCH 8/8] remove unused deps --- Cargo.lock | 22 ---------------------- Cargo.toml | 2 -- 2 files changed, 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1daa4d7..0832122f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,9 +279,7 @@ dependencies = [ "hvm64-util", "insta", "libloading", - "ordered-float", "serial_test", - "thiserror", ] [[package]] @@ -635,26 +633,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "thiserror" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index ffeb91ab..b787d5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,8 +35,6 @@ debug = "full" [dependencies] clap = { version = "4.5.4", features = ["derive"] } libloading = { version = "0.8.3", default-features = false } -ordered-float = { version = "4.2.0", default-features = false } -thiserror = "1.0.58" hvm64-ast = { path = "./ast" } hvm64-runtime = { path = "./runtime" }