-
Notifications
You must be signed in to change notification settings - Fork 403
/
interop.rs
91 lines (75 loc) · 2.81 KB
/
interop.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use crate::hvm::*;
// Abstract Global Net
// Allows any global net to be read back
// -------------
pub trait NetReadback {
fn enter(&self, var: Port) -> Port;
fn node_load(&self, loc: usize) -> Pair;
}
impl<'a> NetReadback for GNet<'a> {
fn enter(&self, var: Port) -> Port { self.enter(var) }
fn node_load(&self, loc: usize) -> Pair { self.node_load(loc) }
}
// Global Net equivalent to the C implementation.
// NOTE: If the C struct `Net` changes, this has to change as well.
// TODO: use `bindgen` crate (https://github.com/rust-lang/rust-bindgen) to generate C structs
// -------------
#[repr(C)]
pub struct NetC {
pub node: [APair; NetC::G_NODE_LEN], // global node buffer
pub vars: [APort; NetC::G_VARS_LEN], // global vars buffer
pub rbag: [APair; NetC::G_RBAG_LEN], // global rbag buffer
pub itrs: AtomicU64, // interaction count
pub idle: AtomicU32, // idle thread counter
}
impl NetC {
// Constants relevant in the C implementation
// NOTE: If any of these constants are changed in C, they have to be changed here as well.
pub const TPC_L2: usize = 3;
pub const TPC: usize = 1 << NetC::TPC_L2;
pub const CACHE_PAD: usize = 64; // Cache padding
pub const HLEN: usize = 1 << 16; // max 16k high-priority redexes
pub const RLEN: usize = 1 << 24; // max 16m low-priority redexes
pub const G_NODE_LEN: usize = 1 << 29; // max 536m nodes
pub const G_VARS_LEN: usize = 1 << 29; // max 536m vars
pub const G_RBAG_LEN: usize = NetC::TPC * NetC::RLEN;
pub fn vars_exchange(&self, var: usize, val: Port) -> Port {
Port(self.vars[var].0.swap(val.0, Ordering::Relaxed) as u32)
}
pub fn vars_take(&self, var: usize) -> Port {
self.vars_exchange(var, Port(0))
}
fn node_load(&self, loc:usize) -> Pair {
Pair(self.node[loc].0.load(Ordering::Relaxed))
}
fn enter(&self, mut var: Port) -> Port {
// While `B` is VAR: extend it (as an optimization)
while var.get_tag() == VAR {
// Takes the current `B` substitution as `B'`
let val = self.vars_exchange(var.get_val() as usize, NONE);
// If there was no `B'`, stop, as there is no extension
if val == NONE || val == Port(0) {
break;
}
// Otherwise, delete `B` (we own both) and continue as `A ~> B'`
self.vars_take(var.get_val() as usize);
var = val;
}
return var;
}
}
impl NetReadback for NetC {
fn node_load(&self, loc:usize) -> Pair {
self.node_load(loc)
}
fn enter(&self, var: Port) -> Port {
self.enter(var)
}
}
// Global Net equivalent to the CUDA implementation.
// NOTE: If the CUDA struct `Net` changes, this has to change as well.
// -------------
// TODO
// Problem: CUDA's `GNet` is allocated using `cudaMalloc`
// Solution: Write a CUDA kernel to compact GPU memory and then `memcpy` it to RAM