diff --git a/src/host/readback.rs b/src/host/readback.rs index 72126391..9d755b4b 100644 --- a/src/host/readback.rs +++ b/src/host/readback.rs @@ -26,6 +26,7 @@ impl Host { net } } + /// See [`Host::readback`]. struct ReadbackState<'a> { host: &'a Host, diff --git a/src/main.rs b/src/main.rs index f5f7b9e2..6edcb866 100644 --- a/src/main.rs +++ b/src/main.rs @@ -353,7 +353,7 @@ fn reduce_exprs(host: &Host, exprs: &[Net], opts: &RuntimeOpts) { for expr in exprs { let mut net = DynNet::new(&heap, opts.lazy_mode); dispatch_dyn_net!(&mut net => { - host.encode_net(net, Trg::port(run::Port::new_var(net.root.addr())), expr); + host.encode_net(net, Trg::port(net.root.as_var()), expr); let start_time = Instant::now(); if opts.single_core { net.normal(); diff --git a/src/run.rs b/src/run.rs index ae0790cd..ad0a1f4c 100644 --- a/src/run.rs +++ b/src/run.rs @@ -62,6 +62,7 @@ mod net; mod node; mod parallel; mod port; +mod tag; mod wire; pub use addr::*; @@ -72,6 +73,7 @@ pub use linker::*; pub use net::*; pub use node::*; pub use port::*; +pub use tag::*; pub use wire::*; pub type Lab = u16; diff --git a/src/run/addr.rs b/src/run/addr.rs index c27eac8d..f19515f2 100644 --- a/src/run/addr.rs +++ b/src/run/addr.rs @@ -37,17 +37,21 @@ impl Addr { const HALF_MASK: usize = 0b1000; - /// Given an address to one word of a two-word allocation, returns the address - /// of the first word of that allocation. + /// TODO #[inline(always)] - pub(super) fn left_half(&self) -> Self { - Addr(self.0 & !Addr::HALF_MASK) + pub(super) fn floor(&self, align: Align) -> Self { + Addr(self.0 & (usize::MAX << align.tag_bits())) } - /// Given an address to one word of a two-word allocation, returns the address - /// of the other word of that allocation. + /// TODO #[inline(always)] - pub fn other_half(&self) -> Self { - Addr(self.0 ^ Addr::HALF_MASK) + pub(super) fn other(&self, align: Align) -> Self { + Addr(self.0 ^ (1 << align.tag_bits())) + } + + /// TODO + #[inline(always)] + pub(super) fn offset(&self, words: usize) -> Self { + Addr(self.0 + (words << 3)) } } diff --git a/src/run/allocator.rs b/src/run/allocator.rs index 308207a2..95139a53 100644 --- a/src/run/allocator.rs +++ b/src/run/allocator.rs @@ -1,27 +1,17 @@ use super::*; -/// The memory behind a two-word allocation. -/// -/// This must be aligned to 16 bytes so that the left word's address always ends -/// with `0b0000` and the right word's address always ends with `0b1000`. -#[repr(C)] -#[repr(align(16))] -#[derive(Default)] -pub(super) struct Node(pub AtomicU64, pub AtomicU64); - /// The memory buffer backing a [`Net`]. -#[repr(align(16))] -pub struct Heap(pub(super) [Node]); +#[repr(align(64))] +pub struct Heap(pub(super) [AtomicU64]); impl Heap { #[inline] /// Allocate a new heap with a given size in words. pub fn new_words(words: usize) -> Box<Self> { - let nodes = words / 2; unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut( - alloc::alloc(Layout::array::<Node>(nodes).unwrap()) as *mut _, - nodes, + alloc::alloc(Layout::array::<AtomicU64>(words).unwrap().align_to(64).unwrap()) as *mut _, + words, ) as *mut _) } } @@ -37,66 +27,101 @@ pub struct Allocator<'h> { pub(super) tracer: Tracer, pub(super) heap: &'h Heap, pub(super) next: usize, - pub(super) head: Addr, + pub(super) heads: [Addr; 4], } deref!({<'h>} Allocator<'h> => self.tracer: Tracer); +impl Align { + #[inline(always)] + const fn free(self) -> u64 { + (1 << self as u64) << 60 + } +} + +/// Sentinel values used to indicate free memory. +impl Port { + pub(super) const FREE_1: Port = Port(Align1.free()); + pub(super) const FREE_2: Port = Port(Align2.free()); + pub(super) const FREE_4: Port = Port(Align4.free()); + pub(super) const FREE_8: Port = Port(Align8.free()); +} + impl<'h> Allocator<'h> { pub fn new(heap: &'h Heap) -> Self { - Allocator { tracer: Tracer::default(), heap, next: 0, head: Addr::NULL } + Allocator { tracer: Tracer::default(), heap, next: 0, heads: [Addr::NULL; 4] } + } + + fn head(&mut self, align: Align) -> &mut Addr { + unsafe { self.heads.get_unchecked_mut(align as usize) } } - /// Frees one word of a two-word allocation. + fn push_addr(head: &mut Addr, addr: Addr) { + addr.val().store(head.0 as u64, Relaxed); + *head = addr; + } + + /// Frees one word of an allocation of size `alloc_align`. #[inline(always)] - pub fn half_free(&mut self, addr: Addr) { - trace!(self.tracer, addr); - const FREE: u64 = Port::FREE.0; + pub fn free_word(&mut self, mut addr: Addr, alloc_align: Align) { if cfg!(feature = "_fuzz") { if cfg!(not(feature = "_fuzz_no_free")) { - assert_ne!(addr.val().swap(FREE, Relaxed), FREE, "double free"); + let free = Port::FREE_1.0; + assert_ne!(addr.val().swap(free, Relaxed), free, "double free"); } - } else { - addr.val().store(FREE, Relaxed); - if addr.other_half().val().load(Relaxed) == FREE { - trace!(self.tracer, "other free"); - let addr = addr.left_half(); - if addr.val().compare_exchange(FREE, self.head.0 as u64, Relaxed, Relaxed).is_ok() { - let old_head = &self.head; - let new_head = addr; - trace!(self.tracer, "appended", old_head, new_head); - self.head = new_head; - } else { - trace!(self.tracer, "too slow"); - }; + return; + } + let mut align = Align1; + if align == alloc_align { + return Self::push_addr(self.head(align), addr); + } + addr.val().store(align.free(), Relaxed); + while align != alloc_align { + if addr.other(align).val().load(Relaxed) != align.free() { + return; + } + trace!(self.tracer, "other free"); + let next_align = unsafe { align.next().unwrap_unchecked() }; + addr = addr.floor(next_align); + let next_value = if next_align == alloc_align { self.head(alloc_align).0 as u64 } else { next_align.free() }; + if addr.val().compare_exchange(align.free(), next_value, Relaxed, Relaxed).is_err() { + return trace!(self.tracer, "too slow"); } + trace!(self.tracer, "success"); + if next_align == alloc_align { + let old_head = next_value; + let new_head = addr; + trace!(self.tracer, "appended", old_head, new_head); + return *self.head(align) = addr; + } + align = next_align; } } - /// Allocates a two-word node. + /// Allocates a node, with a size specified by `align`. #[inline(never)] - pub fn alloc(&mut self) -> Addr { - trace!(self.tracer, self.head); - let addr = if self.head != Addr::NULL { - let addr = self.head.clone(); - let next = Addr(self.head.val().load(Relaxed) as usize); - trace!(self.tracer, next); - self.head = next; + pub fn alloc(&mut self, align: Align) -> Addr { + let head = self.head(align); + let addr = if *head != Addr::NULL { + let addr = *head; + let next = Addr(head.val().load(Relaxed) as usize); + *head = next; addr } else { let index = self.next; - self.next += 1; - Addr(&self.heap.0.get(index).expect("OOM").0 as *const _ as _) + self.next += 8; + Addr(&self.heap.0.get(index).expect("OOM") as *const _ as _) }; - trace!(self.tracer, addr, self.head); - addr.val().store(Port::LOCK.0, Relaxed); - addr.other_half().val().store(Port::LOCK.0, Relaxed); + trace!(self, addr); + for i in 0 .. align.width() { + addr.offset(i as usize).val().store(Port::LOCK.0, Relaxed); + } addr } #[inline(always)] pub(crate) fn free_wire(&mut self, wire: Wire) { - self.half_free(wire.addr()); + self.free_word(wire.addr(), wire.alloc_align()); } /// If `trg` is a wire, frees the backing memory. diff --git a/src/run/def.rs b/src/run/def.rs index 0c947def..cf9d8473 100644 --- a/src/run/def.rs +++ b/src/run/def.rs @@ -186,7 +186,7 @@ impl<'a, M: Mode> Net<'a, M> { let def = port.addr().def(); - if trg.tag() == Ctr && !def.labs.has(trg.lab()) { + if trg.is_ctr_ish() && !def.labs.has(trg.lab()) { return self.comm02(port, trg); } diff --git a/src/run/instruction.rs b/src/run/instruction.rs index 7aaf3787..feacbf33 100644 --- a/src/run/instruction.rs +++ b/src/run/instruction.rs @@ -112,14 +112,14 @@ impl<'a, M: Mode> Net<'a, M> { #[inline(always)] pub(crate) fn do_ctr(&mut self, lab: Lab, trg: Trg) -> (Trg, Trg) { let port = trg.target(); - if !M::LAZY && port.tag() == Ctr && port.lab() == lab { - trace!(self.tracer, "fast"); + if !M::LAZY && port.is(Tag::Ctr) && port.lab() == lab { + trace!(self, "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.tag() == Num || port.tag() == Ref && lab >= port.lab() { + } else if false && !M::LAZY && port.is(Tag::Num) || port.is(Tag::Ref) && lab >= port.lab() { self.rwts.comm += 1; (Trg::port(port.clone()), Trg::port(port)) } else { @@ -170,7 +170,7 @@ impl<'a, M: Mode> Net<'a, M> { #[inline(always)] pub(crate) fn do_mat(&mut self, trg: Trg) -> (Trg, Trg) { let port = trg.target(); - if !M::LAZY && port.tag() == Num { + if !M::LAZY && port.is(Tag::Num) { self.rwts.oper += 1; self.free_trg(trg); let num = port.num(); @@ -198,13 +198,13 @@ impl<'a, M: Mode> Net<'a, M> { #[inline(always)] pub(crate) fn do_wires(&mut self) -> (Trg, Trg, Trg, Trg) { - let a = self.alloc(); - let b = a.other_half(); + let a = self.alloc(Align2); + let b = a.offset(1); ( - Trg::port(Port::new_var(a.clone())), - Trg::wire(Wire::new(a)), - Trg::port(Port::new_var(b.clone())), - Trg::wire(Wire::new(b)), + Trg::port(Port::new_var(Align2, a.clone())), + Trg::wire(Wire::new(Align2, a)), + Trg::port(Port::new_var(Align2, b.clone())), + Trg::wire(Wire::new(Align2, b)), ) } @@ -213,7 +213,7 @@ impl<'a, M: Mode> Net<'a, M> { #[allow(unused)] // TODO: emit this instruction pub(crate) fn do_mat_con_con(&mut self, trg: Trg, out: Trg) -> (Trg, Trg, Trg) { let port = trg.target(); - if !M::LAZY && trg.target().tag() == Num { + if !M::LAZY && trg.target().is(Tag::Num) { self.rwts.oper += 1; self.free_trg(trg); let num = port.num(); @@ -241,7 +241,7 @@ impl<'a, M: Mode> Net<'a, M> { #[allow(unused)] // TODO: emit this instruction pub(crate) fn do_mat_con(&mut self, trg: Trg, out: Trg) -> (Trg, Trg) { let port = trg.target(); - if trg.target().tag() == Num { + if trg.target().is(Tag::Num) { self.rwts.oper += 1; self.free_trg(trg); let num = port.num(); diff --git a/src/run/interact.rs b/src/run/interact.rs index 80c5e3bb..1590bddd 100644 --- a/src/run/interact.rs +++ b/src/run/interact.rs @@ -4,42 +4,47 @@ impl<'a, M: Mode> Net<'a, M> { /// Performs an interaction between two connected principal ports. #[inline(always)] pub fn interact(&mut self, a: Port, b: Port) { - self.tracer.sync(); - trace!(self.tracer, a, b); + self.sync(); + trace!(self, a, b); + use Tag::*; match (a.tag(), b.tag()) { // not actually an active pair (Var | Red, _) | (_, Var | Red) => unreachable!(), // nil-nil (Ref, Ref | Num) if !a.is_skippable() => self.call(a, b), (Ref | Num, Ref) if !b.is_skippable() => self.call(b, a), - (Num | Ref, Num | Ref) => self.rwts.eras += 1, + (Num | Ref | AdtZ, Num | Ref | AdtZ) => self.rwts.eras += 1, // comm 2/2 - (Ctr, Mat) if a.lab() != 0 => self.comm22(a, b), - (Mat, Ctr) if b.lab() != 0 => self.comm22(a, b), - (Ctr, Op) | (Op, Ctr) => self.comm22(a, b), - (Ctr, Ctr) if a.lab() != b.lab() => self.comm22(a, b), + (Mat | Op, CtrN!() | AdtN!()) => self.comm2N(a, b), + (CtrN!() | AdtN!(), Mat | Op) => self.comm2N(b, a), // anni - (Mat, Mat) | (Op, Op) | (Ctr, Ctr) => self.anni2(a, b), + (CtrN!(), CtrN!()) => self.anniNM(a, b), + (Mat, Mat) | (Op, Op) => self.anni2(a, b), // comm 2/0 - (Ref, Ctr) if b.lab() >= a.lab() => self.comm02(a, b), - (Ctr, Ref) if a.lab() >= b.lab() => self.comm02(b, a), - (Num, Ctr) => self.comm02(a, b), - (Ctr, Num) => self.comm02(b, a), - (Ref, _) if a == Port::ERA => self.comm02(a, b), - (_, Ref) if b == Port::ERA => self.comm02(b, a), + (Ref, CtrN!() | AdtN!()) if b.lab() >= a.lab() => self.comm0N(a, b), + (CtrN!() | AdtN!(), Ref) if a.lab() >= b.lab() => self.comm0N(b, a), + (Num, CtrN!() | AdtN!()) => self.comm0N(a, b), + (CtrN!() | AdtN!(), Num) => self.comm0N(b, a), + (AdtZ, Op | Mat) => self.comm02(a, b), + (Op | Mat, AdtZ) => self.comm02(a, b), + // TODO + (AdtN!() | AdtZ, CtrN!()) => self.adt_ctr(a, b), + (CtrN!(), AdtN!() | AdtZ) => self.adt_ctr(b, a), + (AdtN!() | AdtZ, AdtN!() | AdtZ) => todo!(), // deref - (Ref, _) => self.call(a, b), - (_, Ref) => self.call(b, a), + (Ref, Mat | Op) if a == Port::ERA => self.comm02(a, b), + (Mat | Op, Ref) if b == Port::ERA => self.comm02(b, a), + (Ref, CtrN!() | AdtN!()) if a == Port::ERA => self.comm0N(a, b), + (CtrN!() | AdtN!(), Ref) if b == Port::ERA => self.comm0N(b, a), + (Ref, CtrN!() | AdtN!() | Mat | Op) => self.call(a, b), + (CtrN!() | AdtN!() | Mat | Op, Ref) => self.call(b, a), // native ops - (Op, Num) => self.op_num(a, b), - (Num, Op) => self.op_num(b, a), + (Op, Num) => self.opr_num(a, b), + (Num, Op) => self.opr_num(b, a), (Mat, Num) => self.mat_num(a, b), (Num, Mat) => self.mat_num(b, a), // todo: what should the semantics of these be? - (Mat, Ctr) // b.lab() == 0 - | (Ctr, Mat) // a.lab() == 0 - | (Op, Mat) - | (Mat, Op) => unimplemented!("{:?}-{:?}", a.tag(), b.tag()), + (Op, Mat) | (Mat, Op) => unimplemented!("{:?}-{:?}", a.tag(), b.tag()), } } @@ -72,7 +77,7 @@ impl<'a, M: Mode> Net<'a, M> { /// ``` #[inline(never)] pub fn anni2(&mut self, a: Port, b: Port) { - trace!(self.tracer, a, b); + trace!(self, a, b); self.rwts.anni += 1; let a = a.consume_node(); let b = b.consume_node(); @@ -119,7 +124,7 @@ impl<'a, M: Mode> Net<'a, M> { /// ``` #[inline(never)] pub fn comm22(&mut self, a: Port, b: Port) { - trace!(self.tracer, a, b); + trace!(self, a, b); self.rwts.comm += 1; let a = a.consume_node(); @@ -130,13 +135,13 @@ impl<'a, M: Mode> Net<'a, M> { let B1 = self.create_node(b.tag, b.lab); let B2 = self.create_node(b.tag, b.lab); - trace!(self.tracer, A1.p0, A2.p0, B1.p0, B2.p0); + trace!(self, A1.p0, A2.p0, B1.p0, B2.p0); self.link_port_port(A1.p1, B1.p1); self.link_port_port(A1.p2, B2.p1); self.link_port_port(A2.p1, B1.p2); self.link_port_port(A2.p2, B2.p2); - trace!(self.tracer); + trace!(self); self.link_wire_port(a.p1, B1.p0); self.link_wire_port(a.p2, B2.p0); self.link_wire_port(b.p1, A1.p0); @@ -165,7 +170,7 @@ impl<'a, M: Mode> Net<'a, M> { /// ``` #[inline(never)] pub fn comm02(&mut self, a: Port, b: Port) { - trace!(self.tracer, a, b); + trace!(self, a, b); self.rwts.comm += 1; let b = b.consume_node(); self.link_wire_port(b.p1, a.clone()); @@ -204,20 +209,20 @@ impl<'a, M: Mode> Net<'a, M> { /// ``` #[inline(never)] pub fn mat_num(&mut self, a: Port, b: Port) { - trace!(self.tracer, a, b); + trace!(self, a, b); self.rwts.oper += 1; let a = a.consume_node(); let b = b.num(); if b == 0 { let x = self.create_node(Ctr, 0); - trace!(self.tracer, x.p0); + trace!(self, x.p0); self.link_port_port(x.p2, Port::ERA); self.link_wire_port(a.p2, x.p1); self.link_wire_port(a.p1, x.p0); } else { - let x = self.create_node(Ctr, 0); - let y = self.create_node(Ctr, 0); - trace!(self.tracer, x.p0, y.p0); + let x = self.create_node(Tag::Ctr, 0); + let y = self.create_node(Tag::Ctr, 0); + trace!(self, x.p0, y.p0); self.link_port_port(x.p1, Port::ERA); self.link_port_port(x.p2, y.p0); self.link_port_port(y.p1, Port::new_num(b - 1)); @@ -253,21 +258,37 @@ impl<'a, M: Mode> Net<'a, M> { /// ``` #[inline(never)] pub fn op_num(&mut self, a: Port, b: Port) { - trace!(self.tracer, a, b); + trace!(self, a, b); let a = a.consume_node(); let op = unsafe { Op::from_unchecked(a.lab) }; let a1 = a.p1.load_target(); - if a1.tag() == Num { + if a1.is(Tag::Num) { self.rwts.oper += 1; let out = op.op(b.num(), a1.num()); self.link_wire_port(a.p2, Port::new_num(out)); } else { let op = op.swap(); - let x = self.create_node(Op, op as u16); - trace!(self.tracer, x.p0); + let x = self.create_node(Tag::Op, op as u16); + trace!(self, x.p0); self.link_port_port(x.p1, b); self.link_wire_port(a.p2, x.p2); self.link_wire_port(a.p1, x.p0); } } + + fn comm2N(&mut self, a: Port, b: Port) { + todo!() + } + + fn anniNM(&mut self, a: Port, b: Port) { + todo!() + } + + fn comm0N(&mut self, a: Port, b: Port) { + todo!() + } + + fn adt_ctr(&mut self, b: Port, a: Port) { + todo!() + } } diff --git a/src/run/net.rs b/src/run/net.rs index 5ecbed83..da04bb76 100644 --- a/src/run/net.rs +++ b/src/run/net.rs @@ -16,8 +16,8 @@ deref!({<'a, M: Mode>} Net<'a, M> => self.linker: Linker<'a, M>); impl<'h, M: Mode> Net<'h, M> { /// Creates an empty net with a given heap. pub fn new(heap: &'h Heap) -> Self { - let mut net = Net::new_with_root(heap, Wire(std::ptr::null())); - net.root = Wire::new(net.alloc()); + let mut net = Net::new_with_root(heap, Wire(0)); + net.root = Wire::new(Align2, net.alloc(Align2)); net } @@ -55,13 +55,13 @@ impl<'a, M: Mode> Net<'a, M> { let mut path: Vec<Port> = vec![]; loop { - trace!(self.tracer, prev); + trace!(self, prev); // Load ptrs let next = self.get_target_full(prev.clone()); - trace!(self.tracer, next); + trace!(self, next); // If next is root, stop. - if next == Port::new_var(root.addr()) || next == Port::new_var(self.root.addr()) { + if next == root.as_var() || next == self.root.as_var() { break; } @@ -73,7 +73,7 @@ impl<'a, M: Mode> Net<'a, M> { prev = path.pop().unwrap(); continue; // Otherwise, if it is a ref, expand it. - } else if next.tag() == Ref && next != Port::ERA { + } else if next.is(Tag::Ref) && next != Port::ERA { self.call(next, prev.clone()); continue; // Otherwise, we're done. @@ -83,7 +83,7 @@ impl<'a, M: Mode> Net<'a, M> { } // If next is an aux port, pass through. - let main = self.get_header(next.addr().left_half()); + let main = self.get_header(next.addr().floor(next.alloc_align())); path.push(prev); prev = main.this.clone(); } @@ -93,15 +93,16 @@ impl<'a, M: Mode> Net<'a, M> { pub fn normal_from(&mut self, root: Wire) { assert!(M::LAZY); - let mut visit = vec![Port::new_var(root.addr())]; + let mut visit = vec![root.as_var()]; while let Some(prev) = visit.pop() { - trace!(self.tracer, "visit", prev); + trace!(self, "visit", prev); //println!("normal {} | {}", prev.view(), self.rewrites()); let next = self.weak_normal(prev, root.clone()); - trace!(self.tracer, "got", next); + trace!(self, "got", next); if next.is_full_node() { - visit.push(Port::new_var(next.addr())); - visit.push(Port::new_var(next.addr().other_half())); + todo!(); + // visit.push(Port::new_var( next.addr())); + // visit.push(Port::new_var( next.addr().other_half())); } } } diff --git a/src/run/node.rs b/src/run/node.rs index 60b27e2e..822b96d6 100644 --- a/src/run/node.rs +++ b/src/run/node.rs @@ -19,8 +19,8 @@ impl Port { TraverseNode { tag: self.tag(), lab: self.lab(), - p1: Wire::new(self.addr()), - p2: Wire::new(self.addr().other_half()), + p1: Wire::new(self.align(), self.addr()), + p2: Wire::new(self.align(), self.addr().other(Align1)), } } } @@ -34,30 +34,29 @@ pub struct CreatedNode { impl<'a, M: Mode> Net<'a, M> { #[inline(always)] pub fn create_node(&mut self, tag: Tag, lab: Lab) -> CreatedNode { - let addr = self.alloc(); + assert_eq!(tag.width(), 2); + let addr = self.alloc(tag.align()); CreatedNode { p0: Port::new(tag, lab, addr.clone()), - p1: Port::new_var(addr.clone()), - p2: Port::new_var(addr.other_half()), + p1: Port::new_var(Align2, addr), + p2: Port::new_var(Align2, addr.other(Align1)), } } /// Creates a wire an aux port pair. #[inline(always)] pub fn create_wire(&mut self) -> (Wire, Port) { - let addr = self.alloc(); - self.half_free(addr.other_half()); - (Wire::new(addr.clone()), Port::new_var(addr)) + let addr = self.alloc(Align2); + (Wire::new(Align1, addr), Port::new_var(Align1, addr)) } /// Creates a wire pointing to a given port; sometimes necessary to avoid /// deadlock. #[inline(always)] pub fn create_wire_to(&mut self, port: Port) -> Wire { - let addr = self.alloc(); - self.half_free(addr.other_half()); - let wire = Wire::new(addr); - self.link_port_port(port, Port::new_var(wire.addr())); + let addr = self.alloc(Align1); + let wire = Wire::new(Align1, addr); + self.link_port_port(port, wire.as_var()); wire } } diff --git a/src/run/parallel.rs b/src/run/parallel.rs index 1fc97fde..2695c786 100644 --- a/src/run/parallel.rs +++ b/src/run/parallel.rs @@ -10,7 +10,7 @@ impl<'h, M: Mode> Net<'h, M> { let area = unsafe { std::mem::transmute(&self.heap.0[heap_start .. heap_start + heap_size]) }; let mut net = Net::new_with_root(area, self.root.clone()); net.next = self.next.saturating_sub(heap_start); - net.head = if tid == 0 { net.head } else { Addr::NULL }; + net.heads = if tid == 0 { net.heads } else { [Addr::NULL; 4] }; net.tid = tid; net.tids = tids; net.tracer.set_tid(tid); diff --git a/src/run/port.rs b/src/run/port.rs index 34e6da95..e7376b9a 100644 --- a/src/run/port.rs +++ b/src/run/port.rs @@ -5,6 +5,8 @@ use super::*; /// The type of a port is determined by its *tag*, which is stored in the bottom /// three bits. /// +/// TODO: update +/// /// All tags other than [`Num`] divide the bits of the port as follows: /// - the top 16 bits are the *label*, accessible with [`Port::lab`] /// - the middle 45 bits are the non-alignment bits of the *address*, an @@ -18,86 +20,23 @@ use super::*; #[must_use] pub struct Port(pub u64); -bi_enum! { - #[repr(u8)] - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub enum Tag { - /// `Red` ports represent redirects, which are an implementation detail of - /// the atomic linking algorithm, and don't have a precise analogue in - /// interaction nets. - /// - /// These ports are never directly held, but rather replace the backlinks of - /// some var ports. They are used to resolve inter-thread conflicts, and - /// thus will never appear when single-threaded. - /// - /// See the documentation for the linking algorithm for more. - Red = 0, - /// A `Var` port represents an auxiliary port in the net. - /// - /// The address of this port represents the wire leaving this port, - /// accessible with `Port::wire`. - /// - /// The label of this port is currently unused and always 0. - Var = 1, - /// A `Ref` port represents the principal port of a nilary reference node. - /// - /// The address of this port is a pointer to the corresponding [`Def`]. - /// - /// The label of this port is always equivalent to `def.labs.min_safe`, and - /// is used as an optimization for the ref commutation interaction. - /// - /// Eraser nodes are represented by a null-pointer `Ref`, available as the - /// constant [`Port::ERA`]. - Ref = 2, - /// A `Num` port represents the principal port of a U60 node. - /// - /// The top 60 bits of the port are the value of this node, and are - /// accessible with [`Port::num`]. - /// - /// The 4th bit from the bottom is currently unused in this port. - Num = 3, - /// An `Op` port represents the principal port of an Op node. - /// - /// The label of this port is the corresponding operation, which can be - /// accessed with [`Port::op`]. - /// - /// The address of this port is the address of a two-word allocation, - /// storing the targets of the wires connected to the two auxiliary ports of - /// this node. - Op = 4, - /// A `Mat` port represents the principal port of a Mat node. - /// - /// The address of this port is the address of a two-word allocation, - /// storing the targets of the wires connected to the two auxiliary ports of - /// the node. - /// - /// The label of this port is currently unused and always 0. - Mat = 6, - /// A `Ctr` port represents the principal port of an binary interaction - /// combinator node. - /// - /// The label of this port is the label of the combinator; two combinators - /// annihilate if they have the same label, or commute otherwise. - /// - /// The address of this port is the address of a two-word allocation, - /// storing the targets of the wires connected to the two auxiliary ports of - /// the node. - Ctr = 7, - } -} - impl fmt::Debug for Port { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:016x?} ", self.0)?; match *self { Port::ERA => write!(f, "[ERA]"), - Port::FREE => write!(f, "[FREE]"), + Port::FREE_1 => write!(f, "[FREE 1]"), + Port::FREE_2 => write!(f, "[FREE 2]"), + Port::FREE_4 => write!(f, "[FREE 4]"), + Port::FREE_8 => write!(f, "[FREE 8]"), Port::GONE => write!(f, "[GONE]"), Port::LOCK => write!(f, "[LOCK]"), _ => match self.tag() { - Num => write!(f, "[Num {}]", self.num()), - Var | Red | Mat => write!(f, "[{:?} {:?}]", self.tag(), self.addr()), - Op | Ctr | Ref => write!(f, "[{:?} {:?} {:?}]", self.tag(), self.lab(), self.addr()), + Tag::Num => write!(f, "[Num {}]", self.num()), + Tag::Var | Tag::Red | Tag::Mat => write!(f, "[{:?} {:?}]", self.tag(), self.addr()), + Tag::Op | CtrN!() | AdtN!() | Tag::AdtZ | Tag::Ref => { + write!(f, "[{:?} {:?} {:?}]", self.tag(), self.lab(), self.addr()) + } }, } } @@ -105,10 +44,7 @@ impl fmt::Debug for Port { impl Port { /// The principal port of an eraser node. - pub const ERA: Port = Port(Ref as _); - /// A sentinel value used to indicate free memory; see the allocator for more - /// details. - pub const FREE: Port = Port(0x8000_0000_0000_0000); + pub const ERA: Port = Port(Tag::Ref as _); /// A sentinel value used to lock a wire; see the linking algorithm for more /// details. pub const LOCK: Port = Port(0xFFFF_FFFF_FFFF_FFF0); @@ -124,30 +60,62 @@ impl Port { /// Creates a new [`Var`] port with a given addr. #[inline(always)] - pub fn new_var(addr: Addr) -> Self { - Port::new(Var, 0, addr) + pub fn new_var(alloc_align: Align, addr: Addr) -> Self { + Port::new(Tag::Var, alloc_align as u16, addr) + } + + /// Creates a new [`Red`] port with a given addr. + #[inline(always)] + pub fn new_red(alloc_align: Align, addr: Addr) -> Self { + Port::new(Tag::Red, alloc_align as u16, addr) } /// Creates a new [`Num`] port with a given 60-bit numeric value. #[inline(always)] pub const fn new_num(val: u64) -> Self { - Port((val << 4) | (Num as u64)) + Port((val << 4) | (Tag::Num as u64)) } /// Creates a new [`Ref`] port corresponding to a given definition. #[inline(always)] pub fn new_ref(def: &Def) -> Port { - Port::new(Ref, def.labs.min_safe, Addr(def as *const _ as _)) + Port::new(Tag::Ref, def.labs.min_safe, Addr(def as *const _ as _)) + } + + /// TODO + #[inline(always)] + pub fn new_adtz(variant_count: u8, variant_index: u8) -> Self { + Port(Tag::AdtZ as u64 | ((variant_count as u64) << 16) | ((variant_index as u64) << 8)) + } + + /// TODO + #[inline(always)] + pub fn variant_count(&self) -> u8 { + (self.0 >> 16) as u8 + } + + /// TODO + #[inline(always)] + pub fn variant_index(&self) -> u8 { + (self.0 >> 8) as u8 + } + + /// TODO + #[inline(always)] + pub fn align(&self) -> Align { + unsafe { Align::from_unchecked((self.0 & 0b11) as u8) } } /// Accesses the tag of this port; this is valid for all ports. #[inline(always)] pub fn tag(&self) -> Tag { - unsafe { Tag::from_unchecked((self.0 & 0x7) as u8) } + unsafe { Tag::from_unchecked((self.0 & (1 << self.align().tag_bits()) - 1) as u8) } } + /// TODO #[inline(always)] pub fn is(&self, tag: Tag) -> bool { + // TODO: optimize self.tag() == tag } @@ -160,11 +128,11 @@ impl Port { /// Accesses the addr of this port; this is valid for all non-`Num` ports. #[inline(always)] pub const fn addr(&self) -> Addr { + // todo Addr((self.0 & 0x0000_FFFF_FFFF_FFF8) as usize as _) } - /// Accesses the operation of this port; this is valid for [`Op1`] and [`Op2`] - /// ports. + /// Accesses the operation of this port; this is valid for [`Opr`] ports. #[inline(always)] pub fn op(&self) -> Op { unsafe { Op::from_unchecked(self.lab()) } @@ -180,12 +148,12 @@ impl Port { /// non-sentinel [`Red`] ports. #[inline(always)] pub fn wire(&self) -> Wire { - Wire::new(self.addr()) + Wire::new(self.alloc_align(), self.addr()) } #[inline(always)] pub fn is_principal(&self) -> bool { - self.tag() >= Ref + self.align() != Align1 } /// Given a principal port, returns whether this principal port may be part of @@ -193,22 +161,37 @@ impl Port { /// need to be added to the redex list. #[inline(always)] pub fn is_skippable(&self) -> bool { - self.tag() == Num || self.tag() == Ref && self.lab() != u16::MAX + self.is(Tag::AdtZ) || self.is(Tag::Num) || self.is(Tag::Ref) && self.lab() != u16::MAX } /// Converts a [`Var`] port into a [`Red`] port with the same address. #[inline(always)] pub(super) fn redirect(&self) -> Port { - Port::new(Red, 0, self.addr()) + Port(self.0 ^ (Tag::Red as u64 ^ Tag::Var as u64)) } /// Converts a [`Red`] port into a [`Var`] port with the same address. #[inline(always)] pub(super) fn unredirect(&self) -> Port { - Port::new(Var, 0, self.addr()) + self.redirect() } pub(super) fn is_full_node(&self) -> bool { - self.tag() > Num + match self.tag() { + Tag::Op | Tag::Mat | CtrN!() | AdtN!() => true, + Tag::Red | Tag::Var | Tag::Num | Tag::Ref | Tag::AdtZ => false, + } + } + + /// TODO + #[inline(always)] + pub(super) fn alloc_align(&self) -> Align { + unsafe { Align::from_unchecked(self.lab() as u8) } + } + + /// TODO + #[inline(always)] + pub(super) fn is_ctr_ish(&self) -> bool { + (self.0 * 0b111) > 0b100 } } diff --git a/src/run/tag.rs b/src/run/tag.rs new file mode 100644 index 00000000..3042baff --- /dev/null +++ b/src/run/tag.rs @@ -0,0 +1,123 @@ +use super::*; + +bi_enum! { + #[repr(u8)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub enum Tag { + //-- Align1 --\\ + Red = 0b0_00, + Var = 0b1_00, + + //-- Align2 --\\ + Ctr2 = 0b01_01, + Adt2 = 0b11_01, + Num = 0b00_01, + Op = 0b10_01, + + //-- Align4 --\\ + Ctr3 = 0b001_10, + Ctr4 = 0b101_10, + Adt3 = 0b011_10, + Adt4 = 0b111_10, + Ref = 0b000_10, + Mat = 0b010_10, + AdtZ = 0b100_10, + // = 0b110_10, + + //-- Align8 --\\ + Ctr5 = 0b0001_11, + Ctr6 = 0b0101_11, + Ctr7 = 0b1001_11, + Ctr8 = 0b1101_11, + Adt5 = 0b0011_11, + Adt6 = 0b0111_11, + Adt7 = 0b1011_11, + Adt8 = 0b1111_11, + // = 0b0000_11, + // = 0b0010_11, + // = 0b0100_11, + // = 0b0110_11, + // = 0b1000_11, + // = 0b1010_11, + // = 0b1100_11, + // = 0b1110_11, + } +} + +impl Tag { + #[inline(always)] + pub(super) fn align(self) -> Align { + unsafe { Align::from_unchecked(self as u8 & 0b11) } + } + #[inline] + pub(super) fn width(self) -> u8 { + match self { + Tag::Num | Tag::Ref | Tag::AdtZ => 0, + Tag::Red | Tag::Var => 1, + Tag::Op | Tag::Mat => 2, + CtrN!() | AdtN!() => (1 << (self.align() as u8 - 1)) + 1 + (self as u8 >> 4), + } + } + #[inline] + pub(super) fn arity(self) -> u8 { + match self { + AdtN!() => self.width() - 1, + _ => self.width(), + } + } +} + +macro_rules! CtrN { + () => { + Tag::Ctr2 | Tag::Ctr3 | Tag::Ctr4 | Tag::Ctr5 | Tag::Ctr6 | Tag::Ctr7 | Tag::Ctr8 + }; +} + +macro_rules! AdtN { + () => { + Tag::Adt2 | Tag::Adt3 | Tag::Adt4 | Tag::Adt5 | Tag::Adt6 | Tag::Adt7 | Tag::Adt8 + }; +} + +pub(crate) use AdtN; +pub(crate) use CtrN; + +bi_enum! { + #[repr(u8)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub(crate) enum Align { + Align1 = 0b00, + Align2 = 0b01, + Align4 = 0b10, + Align8 = 0b11, + } +} + +pub(crate) use Align::*; + +impl Align { + #[inline(always)] + pub(super) fn tag_bits(self) -> u8 { + self as u8 + 3 + } + #[inline(always)] + pub(super) fn width(self) -> u8 { + 1 << self as u8 + } + #[inline(always)] + pub(super) fn next(self) -> Option<Self> { + match self { + Align1 => Some(Align2), + Align2 => Some(Align4), + Align4 => Some(Align8), + Align8 => None, + } + } +} + +#[test] +fn test_tag_width() { + use Tag::*; + assert_eq!([Ctr2, Ctr3, Ctr4, Ctr5, Ctr6, Ctr7, Ctr8].map(Tag::width), [2, 3, 4, 5, 6, 7, 8]); + assert_eq!([Adt2, Adt3, Adt4, Adt5, Adt6, Adt7, Adt8].map(Tag::width), [2, 3, 4, 5, 6, 7, 8]); +} diff --git a/src/run/wire.rs b/src/run/wire.rs index 7a79cd94..d27ee5ad 100644 --- a/src/run/wire.rs +++ b/src/run/wire.rs @@ -13,7 +13,7 @@ use super::*; /// Changes to the target are handled by the linker. #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[must_use] -pub struct Wire(pub *const AtomicU64); +pub struct Wire(pub u64); impl fmt::Debug for Wire { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -27,28 +27,33 @@ unsafe impl Sync for Wire {} impl Wire { #[inline(always)] pub fn addr(&self) -> Addr { - Addr(self.0 as _) + Addr((self.0 & 0x0000_FFFF_FFFF_FFFF) as usize) } #[inline(always)] - pub fn new(addr: Addr) -> Wire { - Wire(addr.0 as _) + pub fn alloc_align(&self) -> Align { + unsafe { Align::from_unchecked((self.0 >> 48) as u8) } + } + + #[inline(always)] + pub fn new(alloc_align: Align, addr: Addr) -> Wire { + Wire(((alloc_align as u64) << 48) | (addr.0 as u64)) } #[inline(always)] fn target<'a>(&self) -> &'a AtomicU64 { if cfg!(feature = "_fuzz") { - assert_ne!(self.0 as usize, 0xfffffffffff0u64 as usize); - assert_ne!(self.0 as usize, 0); + assert_ne!(self.addr().0, 0xfffffffffff0u64 as usize); + assert_ne!(self.0, 0); } - unsafe { &*self.0 } + unsafe { &*(self.addr().0 as *const _) } } #[inline(always)] pub fn load_target(&self) -> Port { let port = Port(self.target().load(Relaxed)); if cfg!(feature = "_fuzz") { - assert_ne!(port, Port::FREE); + assert_ne!(port, Port::FREE_1); } port } @@ -67,18 +72,23 @@ impl Wire { pub fn swap_target(&self, value: Port) -> Port { let port = Port(self.target().swap(value.0, Relaxed)); if cfg!(feature = "_fuzz") { - assert_ne!(port, Port::FREE); + assert_ne!(port, Port::FREE_1); } port } + #[inline(always)] + pub fn as_var(&self) -> Port { + Port(self.0 | Tag::Var as u64) + } + // Takes a pointer's target. #[inline(always)] pub fn lock_target(&self) -> Port { loop { let got = self.swap_target(Port::LOCK); if cfg!(feature = "_fuzz") { - assert_ne!(got, Port::FREE); + assert_ne!(got, Port::FREE_1); } if got != Port::LOCK { return got; @@ -86,9 +96,4 @@ impl Wire { spin_loop(); } } - - #[inline(always)] - pub(super) fn as_var(&self) -> Port { - Port::new_var(self.addr()) - } } diff --git a/src/trace.rs b/src/trace.rs index 7bb961d1..b7d22d95 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -64,7 +64,7 @@ #![cfg_attr(not(feature = "trace"), allow(unused))] use std::{ - cell::UnsafeCell, + cell::{Cell, UnsafeCell}, fmt::{self, Debug, Formatter, Write}, sync::{ atomic::{AtomicBool, AtomicU64, Ordering}, @@ -84,10 +84,10 @@ pub struct Tracer(()); #[cfg(not(feature = "trace"))] impl Tracer { #[inline(always)] - pub fn sync(&mut self) {} + pub fn sync(&self) {} #[inline(always)] #[doc(hidden)] - pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&mut self, _: A) {} + pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&self, _: A) {} #[inline(always)] pub fn set_tid(&self, _: usize) {} } @@ -124,12 +124,12 @@ pub struct Tracer(TraceWriter); #[cfg(feature = "trace")] impl Tracer { #[inline(always)] - pub fn sync(&mut self) { + pub fn sync(&self) { self.0.sync() } #[inline(always)] #[doc(hidden)] - pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&mut self, args: A) { + pub fn trace<S: TraceSourceBearer, A: TraceArgs>(&self, args: A) { self.0.trace::<S, A>(args) } #[inline(always)] @@ -183,7 +183,7 @@ static ACTIVE_TRACERS: Mutex<Vec<Box<TraceLock>>> = Mutex::new(Vec::new()); struct TraceWriter { lock: &'static TraceLock, - nonce: u64, + nonce: Cell<u64>, } unsafe impl Send for TraceWriter {} @@ -197,13 +197,13 @@ impl Default for TraceWriter { let lock = unsafe { &*(&*boxed as *const _) }; let mut active_tracers = ACTIVE_TRACERS.lock().unwrap(); active_tracers.push(boxed); - TraceWriter { lock, nonce: TRACE_NONCE.fetch_add(1, Ordering::Relaxed) } + TraceWriter { lock, nonce: Cell::new(TRACE_NONCE.fetch_add(1, Ordering::Relaxed)) } } } impl TraceWriter { - fn sync(&mut self) { - self.nonce = TRACE_NONCE.fetch_add(1, Ordering::Relaxed); + fn sync(&self) { + self.nonce.set(TRACE_NONCE.fetch_add(1, Ordering::Relaxed)); } fn acquire(&self, cb: impl FnOnce(&mut TraceData)) { while self.lock.locked.compare_exchange_weak(false, true, Ordering::Relaxed, Ordering::Relaxed).is_err() { @@ -212,13 +212,13 @@ impl TraceWriter { cb(unsafe { &mut *self.lock.data.get() }); self.lock.locked.store(false, Ordering::Release); } - fn trace<S: TraceSourceBearer, A: TraceArgs>(&mut self, args: A) { + fn trace<S: TraceSourceBearer, A: TraceArgs>(&self, args: A) { if cfg!(feature = "_fuzz") { self.sync(); } let meta: &'static _ = &TraceMetadata { source: S::SOURCE, arg_fmts: A::FMTS }; self.acquire(|data| { - let nonce = self.nonce; + let nonce = self.nonce.get(); for arg in args.to_words().rev() { data.write_word(arg); }