pub use crate::runtime::{*};
use std::sync::atomic::{AtomicU8, AtomicU64, AtomicI64, Ordering};
use crossbeam::utils::{CachePadded, Backoff};
pub type Ptr = u64;
pub type AtomicPtr = AtomicU64;
pub type ArityMap = crate::runtime::data::u64_map::U64Map<u64>;
#[derive(Debug)]
pub struct LocalVars {
pub tid: usize,
pub used: AtomicI64, pub next: AtomicU64, pub amin: AtomicU64, pub amax: AtomicU64, pub dups: AtomicU64, pub cost: AtomicU64, }
pub struct Heap {
pub tids: usize,
pub node: Box<[AtomicU64]>,
pub lock: Box<[AtomicU8]>,
pub lvar: Box<[CachePadded<LocalVars>]>,
pub vstk: Box<[VisitQueue]>,
pub aloc: Box<[Box<[AtomicU64]>]>,
pub vbuf: Box<[Box<[AtomicU64]>]>,
pub rbag: RedexBag,
}
pub const VAL: u64 = 1;
pub const EXT: u64 = 0x100000000;
pub const TAG: u64 = 0x1000000000000000;
pub const DP0: u64 = 0x0;
pub const DP1: u64 = 0x1;
pub const VAR: u64 = 0x2;
pub const ARG: u64 = 0x3;
pub const ERA: u64 = 0x4;
pub const LAM: u64 = 0x5;
pub const APP: u64 = 0x6;
pub const SUP: u64 = 0x7;
pub const CTR: u64 = 0x8;
pub const FUN: u64 = 0x9;
pub const OP2: u64 = 0xA;
pub const U60: u64 = 0xB;
pub const F60: u64 = 0xC;
pub const NIL: u64 = 0xF;
pub const ADD: u64 = 0x0;
pub const SUB: u64 = 0x1;
pub const MUL: u64 = 0x2;
pub const DIV: u64 = 0x3;
pub const MOD: u64 = 0x4;
pub const AND: u64 = 0x5;
pub const OR : u64 = 0x6;
pub const XOR: u64 = 0x7;
pub const SHL: u64 = 0x8;
pub const SHR: u64 = 0x9;
pub const LTN: u64 = 0xA;
pub const LTE: u64 = 0xB;
pub const EQL: u64 = 0xC;
pub const GTE: u64 = 0xD;
pub const GTN: u64 = 0xE;
pub const NEQ: u64 = 0xF;
pub fn Var(pos: u64) -> Ptr {
(VAR * TAG) | pos
}
pub fn Dp0(col: u64, pos: u64) -> Ptr {
(DP0 * TAG) | (col * EXT) | pos
}
pub fn Dp1(col: u64, pos: u64) -> Ptr {
(DP1 * TAG) | (col * EXT) | pos
}
pub fn Arg(pos: u64) -> Ptr {
(ARG * TAG) | pos
}
pub fn Era() -> Ptr {
ERA * TAG
}
pub fn Lam(pos: u64) -> Ptr {
(LAM * TAG) | pos
}
pub fn App(pos: u64) -> Ptr {
(APP * TAG) | pos
}
pub fn Sup(col: u64, pos: u64) -> Ptr {
(SUP * TAG) | (col * EXT) | pos
}
pub fn Op2(ope: u64, pos: u64) -> Ptr {
(OP2 * TAG) | (ope * EXT) | pos
}
pub fn U6O(val: u64) -> Ptr {
(U60 * TAG) | val
}
pub fn F6O(val: u64) -> Ptr {
(F60 * TAG) | val
}
pub fn Ctr(fun: u64, pos: u64) -> Ptr {
(CTR * TAG) | (fun * EXT) | pos
}
pub fn Fun(fun: u64, pos: u64) -> Ptr {
(FUN * TAG) | (fun * EXT) | pos
}
pub fn get_tag(lnk: Ptr) -> u64 {
lnk / TAG
}
pub fn get_ext(lnk: Ptr) -> u64 {
(lnk / EXT) & 0xFFF_FFFF
}
pub fn get_val(lnk: Ptr) -> u64 {
lnk & 0xFFFF_FFFF
}
pub fn get_num(lnk: Ptr) -> u64 {
lnk & 0xFFF_FFFF_FFFF_FFFF
}
pub fn get_loc(lnk: Ptr, arg: u64) -> u64 {
get_val(lnk) + arg
}
pub fn get_cost(heap: &Heap) -> u64 {
heap.lvar.iter().map(|x| x.cost.load(Ordering::Relaxed)).sum()
}
pub fn get_used(heap: &Heap) -> i64 {
heap.lvar.iter().map(|x| x.used.load(Ordering::Relaxed)).sum()
}
pub fn inc_cost(heap: &Heap, tid: usize) {
unsafe { heap.lvar.get_unchecked(tid) }.cost.fetch_add(1, Ordering::Relaxed);
}
pub fn gen_dup(heap: &Heap, tid: usize) -> u64 {
return unsafe { heap.lvar.get_unchecked(tid) }.dups.fetch_add(1, Ordering::Relaxed) & 0xFFF_FFFF;
}
pub fn arity_of(arit: &ArityMap, lnk: Ptr) -> u64 {
return *arit.get(&get_ext(lnk)).unwrap_or(&0);
}
pub fn load_ptr(heap: &Heap, loc: u64) -> Ptr {
unsafe { heap.node.get_unchecked(loc as usize).load(Ordering::Relaxed) }
}
pub fn move_ptr(heap: &Heap, old_loc: u64, new_loc: u64) -> Ptr {
link(heap, new_loc, take_ptr(heap, old_loc))
}
pub fn load_arg(heap: &Heap, term: Ptr, arg: u64) -> Ptr {
load_ptr(heap, get_loc(term, arg))
}
pub fn take_ptr(heap: &Heap, loc: u64) -> Ptr {
unsafe { heap.node.get_unchecked(loc as usize).swap(0, Ordering::Relaxed) }
}
pub fn take_arg(heap: &Heap, term: Ptr, arg: u64) -> Ptr {
take_ptr(heap, get_loc(term, arg))
}
pub fn link(heap: &Heap, loc: u64, ptr: Ptr) -> Ptr {
unsafe {
heap.node.get_unchecked(loc as usize).store(ptr, Ordering::Relaxed);
if get_tag(ptr) <= VAR {
let arg_loc = get_loc(ptr, get_tag(ptr) & 0x01);
heap.node.get_unchecked(arg_loc as usize).store(Arg(loc), Ordering::Relaxed);
}
}
ptr
}
pub fn new_atomic_u8_array(size: usize) -> Box<[AtomicU8]> {
return unsafe { Box::from_raw(AtomicU8::from_mut_slice(Box::leak(vec![0xFFu8; size].into_boxed_slice()))) }
}
pub fn new_atomic_u64_array(size: usize) -> Box<[AtomicU64]> {
return unsafe { Box::from_raw(AtomicU64::from_mut_slice(Box::leak(vec![0u64; size].into_boxed_slice()))) }
}
pub fn new_tids(tids: usize) -> Box<[usize]> {
return (0 .. tids).collect::<Vec<usize>>().into_boxed_slice();
}
pub fn new_heap(size: usize, tids: usize) -> Heap {
let mut lvar = vec![];
for tid in 0 .. tids {
lvar.push(CachePadded::new(LocalVars {
tid: tid,
used: AtomicI64::new(0),
next: AtomicU64::new((size / tids * (tid + 0)) as u64),
amin: AtomicU64::new((size / tids * (tid + 0)) as u64),
amax: AtomicU64::new((size / tids * (tid + 1)) as u64),
dups: AtomicU64::new(((1 << 28) / tids * tid) as u64),
cost: AtomicU64::new(0),
}))
}
let node = new_atomic_u64_array(size);
let lock = new_atomic_u8_array(size);
let lvar = lvar.into_boxed_slice();
let rbag = RedexBag::new(tids);
let aloc = (0 .. tids).map(|x| new_atomic_u64_array(1 << 20)).collect::<Vec<Box<[AtomicU64]>>>().into_boxed_slice();
let vbuf = (0 .. tids).map(|x| new_atomic_u64_array(1 << 16)).collect::<Vec<Box<[AtomicU64]>>>().into_boxed_slice();
let vstk = (0 .. tids).map(|x| VisitQueue::new()).collect::<Vec<VisitQueue>>().into_boxed_slice();
return Heap { tids, node, lock, lvar, rbag, aloc, vbuf, vstk };
}
pub fn alloc(heap: &Heap, tid: usize, arity: u64) -> u64 {
unsafe {
let lvar = &heap.lvar.get_unchecked(tid);
if arity == 0 {
0
} else {
let mut length = 0;
loop {
let val = heap.node.get_unchecked(*lvar.next.as_mut_ptr() as usize).load(Ordering::Relaxed);
if val == 0 {
length += 1;
} else {
length = 0;
};
*lvar.next.as_mut_ptr() += 1;
if *lvar.next.as_mut_ptr() >= *lvar.amax.as_mut_ptr() {
length = 0;
*lvar.next.as_mut_ptr() = *lvar.amin.as_mut_ptr();
}
if length == arity {
return *lvar.next.as_mut_ptr() - length;
}
}
}
}
}
pub fn free(heap: &Heap, tid: usize, loc: u64, arity: u64) {
for i in 0 .. arity {
unsafe { heap.node.get_unchecked((loc + i) as usize) }.store(0, Ordering::Relaxed);
}
}
pub fn atomic_relink(heap: &Heap, loc: u64, old: Ptr, neo: Ptr) -> Result<Ptr, Ptr> {
unsafe {
let got = heap.node.get_unchecked(loc as usize).compare_exchange_weak(old, neo, Ordering::Relaxed, Ordering::Relaxed)?;
if get_tag(neo) <= VAR {
let arg_loc = get_loc(neo, get_tag(neo) & 0x01);
heap.node.get_unchecked(arg_loc as usize).store(Arg(loc), Ordering::Relaxed);
}
return Ok(got);
}
}
pub fn atomic_subst(heap: &Heap, arit: &ArityMap, tid: usize, var: Ptr, val: Ptr) {
loop {
let arg_ptr = load_ptr(heap, get_loc(var, get_tag(var) & 0x01));
if get_tag(arg_ptr) == ARG {
if heap.tids == 1 {
link(heap, get_loc(arg_ptr, 0), val);
return;
} else {
if atomic_relink(heap, get_loc(arg_ptr, 0), var, val).is_ok() {
return;
} else {
continue;
}
}
}
if get_tag(arg_ptr) == ERA {
collect(heap, arit, tid, val); return;
}
}
}
pub const LOCK_OPEN : u8 = 0xFF;
pub fn acquire_lock(heap: &Heap, tid: usize, term: Ptr) -> Result<u8, u8> {
let locker = unsafe { heap.lock.get_unchecked(get_loc(term, 0) as usize) };
locker.compare_exchange_weak(LOCK_OPEN, tid as u8, Ordering::Acquire, Ordering::Relaxed)
}
pub fn release_lock(heap: &Heap, tid: usize, term: Ptr) {
let locker = unsafe { heap.lock.get_unchecked(get_loc(term, 0) as usize) };
locker.store(LOCK_OPEN, Ordering::Release)
}
pub fn collect(heap: &Heap, arit: &ArityMap, tid: usize, term: Ptr) {
let mut coll = Vec::new();
let mut next = term;
loop {
let term = next;
match get_tag(term) {
DP0 => {
link(heap, get_loc(term, 0), Era());
if acquire_lock(heap, tid, term).is_ok() {
if get_tag(load_arg(heap, term, 1)) == ERA {
coll.push(take_arg(heap, term, 2));
free(heap, tid, get_loc(term, 0), 3);
}
release_lock(heap, tid, term);
}
}
DP1 => {
link(heap, get_loc(term, 1), Era());
if acquire_lock(heap, tid, term).is_ok() {
if get_tag(load_arg(heap, term, 0)) == ERA {
coll.push(take_arg(heap, term, 2));
free(heap, tid, get_loc(term, 0), 3);
}
release_lock(heap, tid, term);
}
}
VAR => {
link(heap, get_loc(term, 0), Era());
}
LAM => {
atomic_subst(heap, arit, tid, Var(get_loc(term,0)), Era());
next = take_arg(heap, term, 1);
free(heap, tid, get_loc(term, 0), 2);
continue;
}
APP => {
coll.push(take_arg(heap, term, 0));
next = take_arg(heap, term, 1);
free(heap, tid, get_loc(term, 0), 2);
continue;
}
SUP => {
coll.push(take_arg(heap, term, 0));
next = take_arg(heap, term, 1);
free(heap, tid, get_loc(term, 0), 2);
continue;
}
OP2 => {
coll.push(take_arg(heap, term, 0));
next = take_arg(heap, term, 1);
free(heap, tid, get_loc(term, 0), 2);
continue;
}
U60 => {}
F60 => {}
CTR | FUN => {
let arity = arity_of(arit, term);
for i in 0 .. arity {
if i < arity - 1 {
coll.push(take_arg(heap, term, i));
} else {
next = take_arg(heap, term, i);
}
}
free(heap, tid, get_loc(term, 0), arity);
if arity > 0 {
continue;
}
}
_ => {}
}
if let Some(got) = coll.pop() {
next = got;
} else {
break;
}
}
}