initial commit of memory manager

This commit is contained in:
Edward Amsden 2022-03-16 18:28:06 -05:00
parent b70c5d957e
commit 34e2cc6873
No known key found for this signature in database
GPG Key ID: 548EDF608CA956F6
7 changed files with 533 additions and 0 deletions

1
rust/iron-planet/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

54
rust/iron-planet/Cargo.lock generated Normal file
View File

@ -0,0 +1,54 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "iron-planet"
version = "0.1.0"
dependencies = [
"memmap",
"noun",
]
[[package]]
name = "libc"
version = "0.2.120"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09"
[[package]]
name = "memmap"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "noun"
version = "0.1.0"
source = "git+https://github.com/mcevoypeter/urbit.git?branch=main#cca52babc9a080199083519cfb347de8cd3024e0"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -0,0 +1,11 @@
[package]
name = "iron-planet"
version = "0.1.0"
authors = ["Edward Amsden <edward@blackriversoft.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
memmap = "0.6.2"
noun = { git = "https://github.com/mcevoypeter/urbit.git", branch = "main" }

View File

@ -0,0 +1,2 @@
pub mod memory;
pub mod noun;

View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@ -0,0 +1,390 @@
use memmap::MmapMut;
use std::mem;
use std::ptr::copy_nonoverlapping;
/// Tag bits for a direct atom
pub const DIRECT : u64 = 0x0;
/// Tag mask for a direct atom (
pub const DIRECT_MASK : u64 = 0x8000000000000000;
/// Maximum direct atom
pub const DIRECT_MAX : u64 = 0x7FFFFFFFFFFFFFFF;
/// Tag bits for an indirect atom
pub const INDIRECT : u64 = 0x8000000000000000;
/// Tag mask for an indirect atom
pub const INDIRECT_MASK : u64 = 0xC000000000000000;
/// Tag bits for a cell
pub const CELL : u64 = 0xC000000000000000;
/// Tag mask for a cell
pub const CELL_MASK : u64 = 0xE000000000000000;
/// Tag bits for a forwarding pointer
const FORWARD : u64 = 0xE000000000000000;
/// Tag mask for a forwarding pointer
const FORWARD_MASK : u64 = 0xE000000000000000;
/// Mask to extract a pointer if not shifting
pub const PTR_MASK : u64 = 0x1FFFFFFFFFFFFFFF;
/// Current direction of the stack
enum Polarity {
/// Current frame is lowest in high memory
East,
/// Current frame is highest in low memory
West,
}
/// Structure representing a Nock computational stack (1 per thread)
pub struct NockStack {
sp: *mut u64,
fp: *mut u64,
polarity: Polarity,
_map: MmapMut,
}
/// Given the size *in noun-words*, memory-map space for a Nock stack.
pub fn map_nock_stack(size: usize) -> Result<NockStack,std::io::Error> {
let bytesize = size * mem::size_of::<u64>();
match MmapMut::map_anon(bytesize) {
Ok(mut map) => {
unsafe {
let fp : *mut u64 = map.as_mut_ptr() as *mut u64;
let sp : *mut u64 = fp.add(2);
*fp = fp.add(size) as u64;
Ok(
NockStack {
sp : sp,
fp : fp,
polarity : Polarity::West,
_map: map,
}
)
}
},
Err(e) => Err(e),
}
}
unsafe fn slot_west(stack: &NockStack, slot: usize) -> *mut u64 {
stack.fp.add(slot)
}
unsafe fn slot_east(stack: &NockStack, slot: usize) -> *mut u64 {
stack.fp.sub(slot + 1)
}
/// Get a pointer to a slot in a frame
pub unsafe fn slot(stack: &NockStack, slot: usize) -> *mut u64 {
match stack.polarity {
Polarity::West => slot_west(stack, slot),
Polarity::East => slot_east(stack, slot),
}
}
/// Get a pointer to a local variable slot in a frame
pub unsafe fn local(stack: &NockStack, local: usize) -> *mut u64 {
slot(stack, local + 2)
}
fn push_west(stack: &mut NockStack, slots: usize) {
unsafe {
let east_sp_new_fp : *mut u64 = *(slot_west(stack, 0)) as *mut u64;
let new_east_sp : *mut u64 = east_sp_new_fp.sub(slots + 2);
*(east_sp_new_fp.sub(1)) = stack.sp as u64;
*(east_sp_new_fp.sub(2)) = stack.fp as u64;
stack.fp = east_sp_new_fp;
stack.sp = new_east_sp;
stack.polarity = Polarity::East;
}
}
fn push_east(stack: &mut NockStack, slots: usize) {
unsafe {
let west_sp_new_fp : *mut u64 = *(slot_east(stack, 0)) as *mut u64;
let new_west_sp : *mut u64 = west_sp_new_fp.add(slots + 2);
*(west_sp_new_fp) = stack.sp as u64;
*(west_sp_new_fp.add(1)) = stack.fp as u64;
stack.fp = west_sp_new_fp;
stack.sp = new_west_sp;
stack.polarity = Polarity::West;
}
}
/// Push a new frame
pub fn push(stack: &mut NockStack, slots: usize) {
match stack.polarity {
Polarity::West => push_west(stack, slots),
Polarity::East => push_east(stack, slots),
}
}
fn alloc_west(stack: &mut NockStack, size: usize) -> *mut u64 {
unsafe {
let base = stack.sp;
stack.sp = stack.sp.add(size);
base
}
}
fn alloc_east(stack: &mut NockStack, size: usize) -> *mut u64 {
unsafe {
let base = stack.sp.sub(size);
stack.sp = base;
base
}
}
/// Allocate on the stack
pub fn alloc(stack: &mut NockStack, size: usize) -> *mut u64 {
match stack.polarity {
Polarity::West => alloc_west(stack, size),
Polarity::East => alloc_east(stack, size),
}
}
pub fn cell(stack: &mut NockStack, head: u64, tail: u64) -> u64 {
let cell_dest = alloc(stack, 2);
unsafe {
*cell_dest = head;
*(cell_dest.add(1)) = tail;
}
(cell_dest as u64) >> 3 | CELL
}
pub fn direct(atom: u64) -> u64 {
if atom > DIRECT_MAX {
panic!("Tried to make a direct atom larger than DIRECT_MAX");
}
atom
}
pub fn indirect_1(stack: &mut NockStack, atom: u64) -> u64 {
let indirect_dest = alloc(stack, 2);
unsafe {
*indirect_dest = 8;
*(indirect_dest.add(1)) = atom;
}
(indirect_dest as u64) >> 3 | INDIRECT
}
unsafe fn cell_ptr_unchecked(atom: u64) -> *mut u64 {
(atom << 3) as *mut u64
}
unsafe fn cell_head_unchecked(atom: u64) -> u64 {
*((atom << 3) as *const u64)
}
unsafe fn cell_tail_unchecked(atom: u64) -> u64 {
*(((atom << 3) as *const u64).add(1))
}
/// Size in 64-bit words of an indirect atom
unsafe fn indirect_size_unchecked(atom: u64) -> u64 {
*((atom << 3) as *const u64) << 3
}
pub unsafe fn indirect_data_unchecked(atom: u64) -> *const u64 {
(indirect_ptr_unchecked(atom)).add(1)
}
unsafe fn indirect_ptr_unchecked(atom: u64) -> *mut u64 {
(atom << 3) as *mut u64
}
pub fn is_direct(noun: u64) -> bool {
noun & DIRECT_MASK == DIRECT
}
pub fn is_indirect(noun: u64) -> bool {
noun & INDIRECT_MASK == INDIRECT
}
pub fn is_cell(noun: u64) -> bool {
noun & CELL_MASK == CELL
}
fn is_forward(noun: u64) -> bool {
noun & FORWARD_MASK == FORWARD
}
unsafe fn indirect_forwarded_unchecked(noun: u64) -> Option<u64> {
let raw_sz : u64 = *(indirect_ptr_unchecked(noun));
if is_forward(raw_sz) { Some(raw_sz & PTR_MASK | INDIRECT) }
else { None }
}
unsafe fn cell_forwarded_unchecked(noun: u64) -> Option<u64> {
let head : u64 = cell_head_unchecked(noun);
if is_forward(head) { Some(head & PTR_MASK | CELL) }
else { None }
}
unsafe fn copy_east(stack: &mut NockStack, root: u64) -> u64 {
let mut west_sp : *mut u64 = *(slot_east(stack, 0)) as *mut u64;
let lower_bound_inclusive : *const u64 = stack.sp;
let upper_bound_exclusive : *const u64 = stack.fp;
let mut copy_stack_top : *mut u64 = stack.sp;
let res =
if is_direct(root) { root }
else if ((root << 3) as *const u64) < lower_bound_inclusive { root }
else if ((root << 3) as *const u64) >= upper_bound_exclusive { root }
else if is_indirect(root) {
let sz : usize = (indirect_size_unchecked(root) + 1) as usize;
let base : *mut u64 = west_sp;
west_sp = west_sp.add(sz);
copy_nonoverlapping(indirect_ptr_unchecked(root), base, sz);
*(indirect_ptr_unchecked(root)) = (base as u64) >> 3 | FORWARD;
(base as u64) >> 3 | INDIRECT
} else if is_cell(root) {
let base : *mut u64 = west_sp;
west_sp = west_sp.add(2);
copy_stack_top = copy_stack_top.sub(4);
*copy_stack_top = cell_head_unchecked(root);
*(copy_stack_top.add(1)) = base as u64;
*(copy_stack_top.add(2)) = cell_tail_unchecked(root);
*(copy_stack_top.add(3)) = (base.add(1)) as u64;
*(cell_ptr_unchecked(root)) = (base as u64) >> 3 | FORWARD;
(base as u64) >> 3 | CELL
} else { panic!("no tag matches"); };
loop {
if (copy_stack_top as *const u64) == lower_bound_inclusive { break; }
let noun : u64 = *copy_stack_top;
let dest : *mut u64 = *(copy_stack_top.add(1)) as *mut u64;
copy_stack_top = copy_stack_top.add(2);
if is_direct(noun) { *dest = noun }
else if ((noun << 3) as *const u64) < lower_bound_inclusive { *dest = noun }
else if ((noun << 3) as *const u64) >= upper_bound_exclusive { *dest = noun }
else if is_indirect(noun) {
match indirect_forwarded_unchecked(noun) {
Some(fwd) => { *dest = fwd },
None => {
let sz : usize = (indirect_size_unchecked(noun) + 1) as usize;
let base : *mut u64 = west_sp;
west_sp = west_sp.add(sz);
copy_nonoverlapping(indirect_ptr_unchecked(noun), base, sz);
*(indirect_ptr_unchecked(noun)) = (base as u64) >> 3 | FORWARD;
*dest = (base as u64) >> 3 | INDIRECT;
},
}
} else if is_cell(noun) {
match cell_forwarded_unchecked(noun) {
Some(fwd) => { *dest = fwd },
None => {
let base : *mut u64 = west_sp;
west_sp = west_sp.add(2);
copy_stack_top = copy_stack_top.sub(4);
*copy_stack_top = cell_head_unchecked(noun);
*(copy_stack_top.add(1)) = base as u64;
*(copy_stack_top.add(2)) = cell_tail_unchecked(noun);
*(copy_stack_top.add(3)) = (base.add(1)) as u64;
*(cell_ptr_unchecked(noun)) = (base as u64) >> 3 | FORWARD;
*dest = (base as u64) >> 3 | CELL;
}
}
} else { panic!("no tag matches"); }
};
*(slot_east(stack, 0)) = west_sp as u64;
res
}
unsafe fn copy_west(stack: &mut NockStack, root: u64) -> u64 {
let mut east_sp : *mut u64 = *(slot_west(stack, 0)) as *mut u64;
let lower_bound_inclusive : *const u64 = stack.fp;
let upper_bound_exclusive : *const u64 = stack.sp;
let mut copy_stack_top : *mut u64 = stack.sp;
let res =
if is_direct(root) { root }
else if ((root << 3) as *const u64) < lower_bound_inclusive { root }
else if ((root << 3) as *const u64) >= upper_bound_exclusive { root }
else if is_indirect(root) {
let sz : usize = (indirect_size_unchecked(root) + 1) as usize;
east_sp = east_sp.sub(sz);
let base : *mut u64 = east_sp;
copy_nonoverlapping(indirect_ptr_unchecked(root), base, sz);
*(indirect_ptr_unchecked(root)) = (base as u64) >> 3 | FORWARD;
(base as u64) >> 3 | INDIRECT
} else if is_cell(root) {
east_sp = east_sp.sub(2);
let base : *mut u64 = east_sp;
copy_stack_top = copy_stack_top.add(4);
*copy_stack_top = cell_head_unchecked(root);
*(copy_stack_top.add(1)) = base as u64;
*(copy_stack_top.add(2)) = cell_tail_unchecked(root);
*(copy_stack_top.add(3)) = (base.add(1)) as u64;
*(cell_ptr_unchecked(root)) = (base as u64) >> 3 | FORWARD;
(base as u64) >> 3 | CELL
} else { panic!("no tag matches") };
loop {
if (copy_stack_top as *const u64) == upper_bound_exclusive { break; }
let noun : u64 = *copy_stack_top;
let dest : *mut u64 = *(copy_stack_top.add(1)) as *mut u64;
copy_stack_top = copy_stack_top.sub(2);
if is_direct(noun) { *dest = noun }
else if ((noun << 3) as *const u64) < lower_bound_inclusive { *dest = noun }
else if ((noun << 3) as *const u64) >= upper_bound_exclusive { *dest = noun }
else if is_indirect(noun) {
match indirect_forwarded_unchecked(noun) {
Some(fwd) => { *dest = fwd },
None => {
let sz : usize = (indirect_size_unchecked(noun) + 1) as usize;
east_sp = east_sp.sub(sz);
let base : *mut u64 = east_sp;
copy_nonoverlapping(indirect_ptr_unchecked(noun), base, sz);
*(indirect_ptr_unchecked(noun)) = (base as u64) >> 3 | FORWARD;
*dest = (base as u64) >> 3 | INDIRECT;
},
}
} else if is_cell(noun) {
match cell_forwarded_unchecked(noun) {
Some(fwd) => { *dest = fwd },
None => {
east_sp = east_sp.sub(2);
let base : *mut u64 = east_sp;
copy_stack_top = copy_stack_top.add(4);
*copy_stack_top = cell_head_unchecked(noun);
*(copy_stack_top.add(1)) = base as u64;
*(copy_stack_top.add(2)) = cell_tail_unchecked(noun);
*(copy_stack_top.add(3)) = (base.add(1)) as u64;
*(cell_ptr_unchecked(noun)) = (base as u64) >> 3 | FORWARD;
*dest = (base as u64) >> 3 | CELL;
},
}
} else { panic!("no tag matches") }
};
*(slot_west(stack, 0)) = east_sp as u64;
res
}
fn pop_west(stack: &mut NockStack, root: u64) -> u64 {
unsafe {
let res : u64 = copy_west(stack, root);
stack.sp = *(slot_west(stack, 0)) as *mut u64;
stack.fp = *(slot_west(stack, 1)) as *mut u64;
stack.polarity = Polarity::East;
res
}
}
fn pop_east(stack: &mut NockStack, root: u64) -> u64 {
unsafe {
let res : u64 = copy_east(stack, root);
stack.sp = *(slot_east(stack, 0)) as *mut u64;
stack.fp = *(slot_east(stack, 1)) as *mut u64;
stack.polarity = Polarity::West;
res
}
}
pub fn pop(stack: &mut NockStack, root: u64) -> u64 {
match stack.polarity {
Polarity::East => pop_east(stack, root),
Polarity::West => pop_west(stack, root),
}
}

View File

@ -0,0 +1,72 @@
use noun::atom;
use noun::Noun;
use std::rc::Rc;
use std::ptr::copy_nonoverlapping;
use crate::memory;
enum Work {
Head(Rc<Noun>, Rc<Noun>),
Tail(Rc<Noun>),
Cell,
}
fn atom_onto_stack(stack: &mut memory::NockStack, atom: &atom::Atom) -> u64 {
if atom.v().len() == 0 { 0 }
else if atom.v().len() == 1 && atom.v()[0] <= memory::DIRECT_MAX { atom.v()[0] }
else {
let indirect_dest : *mut u64 = memory::alloc(stack, atom.v().len() + 1);
unsafe {
*indirect_dest = atom.v().len() as u64;
copy_nonoverlapping(atom.v().as_ptr(), indirect_dest.add(1), atom.v().len());
}
((indirect_dest as u64) >> 3) | memory::INDIRECT
}
}
pub fn noun_onto_stack(stack: &mut memory::NockStack, noun: &Noun) -> u64 {
let mut work : Vec<Work> = Vec::new();
let mut results : Vec<u64> = Vec::new();
match noun {
Noun::Atom(atom) => { results.push(atom_onto_stack(stack, atom)); },
Noun::Cell(cell) => { work.push(Work::Head(cell.h(), cell.t())) }
}
loop {
match work.pop() {
None => { break; }
Some(work_type) => match work_type {
Work::Head(head_noun, tail_noun) => {
work.push(Work::Tail(tail_noun));
match &*head_noun {
Noun::Atom(atom) => { results.push(atom_onto_stack(stack, &atom)); },
Noun::Cell(cell) => { work.push(Work::Head(cell.h(), cell.t())) }
}
},
Work::Tail(tail_noun) => {
work.push(Work::Cell);
match &*tail_noun {
Noun::Atom(atom) => { results.push(atom_onto_stack(stack, &atom)); },
Noun::Cell(cell) => { work.push(Work::Head(cell.h(), cell.t())) }
}
},
Work::Cell => {
match results.pop() {
None => { panic!("Shouldn't happen: no results when making a cell"); },
Some(tail_noun) => match results.pop() {
None => { panic!("Shouldn't happen: no results when making a cell"); },
Some(head_noun) => {
results.push(memory::cell(stack, head_noun, tail_noun));
},
}
}
}
}
}
};
match results.pop() {
None => { panic!("shouldn't happen: no result to return") },
Some(result) => { result },
}
}