use super::*;
pub(super) struct Header {
pub(super) this: Port,
pub(super) targ: Port,
}
pub struct Linker<'h, M: Mode> {
pub(super) allocator: Allocator<'h>,
pub rwts: Rewrites,
pub redexes: RedexQueue,
headers: IntMap<Addr, Header>,
_mode: PhantomData<M>,
}
deref!({<'h, M: Mode>} Linker<'h, M> => self.allocator: Allocator<'h>);
impl<'h, M: Mode> Linker<'h, M> {
pub fn new(heap: &'h Heap) -> Self {
Linker {
allocator: Allocator::new(heap),
redexes: RedexQueue::default(),
rwts: Default::default(),
headers: Default::default(),
_mode: PhantomData,
}
}
#[inline(always)]
pub fn link_port_port(&mut self, a_port: Port, b_port: Port) {
trace!(self, a_port, b_port);
if a_port.is_principal() && b_port.is_principal() {
self.redux(a_port, b_port);
} else {
self.half_link_port_port(a_port.clone(), b_port.clone());
self.half_link_port_port(b_port, a_port);
}
}
#[inline(always)]
pub fn link_wire_wire(&mut self, a_wire: Wire, b_wire: Wire) {
trace!(self, a_wire, b_wire);
let a_port = a_wire.lock_target();
let b_port = b_wire.lock_target();
trace!(self, a_port, b_port);
if a_port.is_principal() && b_port.is_principal() {
self.free_wire(a_wire);
self.free_wire(b_wire);
self.redux(a_port, b_port);
} else {
self.half_link_wire_port(a_port.clone(), a_wire, b_port.clone());
self.half_link_wire_port(b_port, b_wire, a_port);
}
}
#[inline(always)]
pub fn link_wire_port(&mut self, a_wire: Wire, b_port: Port) {
trace!(self, a_wire, b_port);
let a_port = a_wire.lock_target();
trace!(self, a_port);
if a_port.is_principal() && b_port.is_principal() {
self.free_wire(a_wire);
self.redux(a_port, b_port);
} else {
self.half_link_wire_port(a_port.clone(), a_wire, b_port.clone());
self.half_link_port_port(b_port, a_port);
}
}
#[inline(always)]
pub fn redux(&mut self, a: Port, b: Port) {
trace!(self, a, b);
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 {
if redex_would_shrink(&a, &b) {
self.redexes.fast.push((a, b));
} else {
self.redexes.slow.push((a, b));
}
} else {
self.set_header(a.clone(), b.clone());
self.set_header(b.clone(), a.clone());
}
}
#[inline(always)]
fn half_link_port_port(&mut self, a_port: Port, b_port: Port) {
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);
}
}
#[inline(always)]
fn half_link_wire_port(&mut self, a_port: Port, a_wire: Wire, b_port: Port) {
trace!(self, a_port, a_wire, b_port);
if a_port.is(Tag::Var) {
let got = a_port.wire().cas_target(a_wire.as_var(), b_port.clone());
if got.is_ok() {
trace!(self, "cas ok");
self.free_wire(a_wire);
} else {
let got = got.unwrap_err();
trace!(self, "cas fail", got);
if b_port.is(Tag::Var) {
let port = b_port.redirect();
a_wire.set_target(port);
} else if b_port.is_principal() {
a_wire.set_target(b_port.clone());
self.resolve_redirect_pri(a_port, a_wire, b_port);
} else {
unreachable!();
}
}
} else {
self.free_wire(a_wire);
if M::LAZY {
self.set_header(a_port, b_port);
}
}
}
fn resolve_redirect_pri(&mut self, mut a_port: Port, a_wire: Wire, b_port: Port) {
trace!(self);
loop {
trace!(self, a_port, a_wire, b_port);
let mut t_wire = a_port.wire();
let mut t_port = t_wire.load_target();
trace!(self, t_port);
if t_port == Port::LOCK {
spin_loop();
continue;
}
if t_port.is(Tag::Red) {
self.free_wire(t_wire);
a_port = t_port;
continue;
}
if t_port.is(Tag::Var) {
if t_wire.cas_target(t_port.clone(), b_port.clone()).is_ok() {
trace!(self, "var cas ok");
t_wire = t_port.wire();
t_port = t_wire.load_target();
while t_port != Port::LOCK && t_port.is(Tag::Red) {
trace!(self, t_wire, t_port);
self.free_wire(t_wire);
t_wire = t_port.wire();
t_port = t_wire.load_target();
}
return;
}
trace!(self, "var cas fail");
continue;
}
if t_port.is_principal() || t_port == Port::GONE {
let x_wire = if a_wire < t_wire { a_wire.clone() } else { t_wire.clone() };
let y_wire = if a_wire < t_wire { t_wire.clone() } else { a_wire.clone() };
trace!(self, x_wire, y_wire);
let x_port = x_wire.swap_target(Port::GONE);
if x_port != Port::GONE {
let y_port = y_wire.swap_target(Port::GONE);
trace!(self, "fst", x_wire, y_wire, x_port, y_port);
self.redux(x_port, y_port);
return;
} else {
trace!(self, "snd !!!", x_wire, y_wire);
self.free_wire(x_wire);
while y_wire.cas_target(Port::GONE, Port::LOCK).is_err() {
spin_loop();
}
self.free_wire(y_wire);
return;
}
}
trace!(self, t_port, a_wire, a_port, b_port);
unreachable!()
}
}
#[allow(unused)]
fn resolve_redirect_var(&mut self, _: Port, _: Wire, b_port: Port) {
loop {
let ste_wire = b_port.clone().wire();
let ste_port = ste_wire.load_target();
if ste_port.is(Tag::Var) {
let trg_wire = ste_port.wire();
let trg_port = trg_wire.load_target();
if trg_port.is(Tag::Red) {
let neo_port = trg_port.unredirect();
if ste_wire.cas_target(ste_port, neo_port).is_ok() {
self.free_wire(trg_wire);
continue;
}
}
}
break;
}
}
}
#[derive(Clone)]
pub struct Trg(pub(crate) Port);
impl fmt::Debug for Trg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_wire() { self.clone().as_wire().fmt(f) } else { self.0.fmt(f) }
}
}
impl Trg {
#[inline(always)]
pub fn port(port: Port) -> Self {
Trg(port)
}
#[inline(always)]
pub fn wire(wire: Wire) -> Self {
Trg(Port(wire.0 as u64))
}
#[inline(always)]
pub(super) fn is_wire(&self) -> bool {
self.0.is(Tag::Red)
}
#[inline(always)]
#[allow(clippy::wrong_self_convention)]
pub(super) fn as_wire(self) -> Wire {
Wire(self.0.0 as _)
}
#[inline(always)]
#[allow(clippy::wrong_self_convention)]
pub(super) fn as_port(self) -> Port {
self.0
}
#[inline(always)]
pub fn target(&self) -> Port {
if self.is_wire() { self.clone().as_wire().load_target() } else { self.0.clone() }
}
}
impl<'h, M: Mode> Linker<'h, M> {
#[inline(always)]
pub fn link_trg_port(&mut self, a: Trg, b: Port) {
match a.is_wire() {
true => self.link_wire_port(a.as_wire(), b),
false => self.link_port_port(a.as_port(), b),
}
}
#[inline(always)]
pub fn link_trg(&mut self, a: Trg, b: Trg) {
match (a.is_wire(), b.is_wire()) {
(true, true) => self.link_wire_wire(a.as_wire(), b.as_wire()),
(true, false) => self.link_wire_port(a.as_wire(), b.as_port()),
(false, true) => self.link_wire_port(b.as_wire(), a.as_port()),
(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)]
pub struct RedexQueue {
pub(super) fast: Vec<(Port, Port)>,
pub(super) slow: Vec<(Port, Port)>,
}
impl RedexQueue {
#[inline(always)]
pub fn pop(&mut self) -> Option<(Port, Port)> {
self.fast.pop().or_else(|| self.slow.pop())
}
#[inline(always)]
pub fn len(&self) -> usize {
self.fast.len() + self.slow.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.fast.is_empty() && self.slow.is_empty()
}
#[inline(always)]
pub fn drain(&mut self) -> impl Iterator<Item = (Port, Port)> + '_ {
self.fast.drain(..).chain(self.slow.drain(..))
}
#[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = &(Port, Port)> {
self.fast.iter().chain(self.slow.iter())
}
#[inline(always)]
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (Port, Port)> {
self.fast.iter_mut().chain(self.slow.iter_mut())
}
#[inline(always)]
pub fn clear(&mut self) {
self.fast.clear();
self.slow.clear();
}
}
fn redex_would_shrink(a: &Port, b: &Port) -> bool {
(*a == Port::ERA || *b == Port::ERA)
|| (a.tag() == Tag::Ctr && b.tag() == Tag::Ctr && a.lab() == b.lab())
|| (!(a.tag() == Tag::Ref || b.tag() == Tag::Ref) && (a.is_num() || b.is_num()))
}