From 0d5dc5cedb5040969ae5eb864b275da8a3f5fbc1 Mon Sep 17 00:00:00 2001 From: tjjfvi Date: Mon, 27 May 2024 13:49:38 -0400 Subject: [PATCH] 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() };