codegen: progress re-implementing rust interpreter

This commit is contained in:
Edward Amsden 2024-04-10 20:48:49 -05:00
parent 3204982f37
commit 6e40a1ebee
10 changed files with 504 additions and 20 deletions

View File

@ -1265,7 +1265,7 @@
hill
--
:: codegen interface
:- %1
=+ %1
|%
::
:: core reference

View File

@ -202,11 +202,11 @@
::
:: long: starting label for direct calls axis 2
:: want: input registers for direct calls axis 6
:: walt: input starting registers LR axis 30
:: wish: starting label for indirect calls axis 62
:: sire: input register for indirect calls axis 126
:: will: code table for arm axis 254
:: sans: next SSA register axis 255
:: walt: input starting registers LR axis 14
:: wish: starting label for indirect calls axis 30
:: sire: input register for indirect calls axis 62
:: will: code table for arm axis 126
:: sans: next SSA register axis 127
+$ pile
$: long=bile
want=need

423
rust/ares/src/codegen.rs Normal file
View File

@ -0,0 +1,423 @@
use crate::interpreter::{inc, interpret, Context, Error, Result, BAIL_EXIT};
use crate::jets::seam::util::get_by;
use crate::jets::util::slot;
use crate::mem::NockStack;
use crate::noun::{DirectAtom, Noun, D, NOUN_NONE, T};
use ares_macros::tas;
use std::mem::size_of;
use std::ptr::write_bytes;
use std::slice::{from_raw_parts, from_raw_parts_mut};
#[derive(Copy, Clone)]
struct Frame {
/// Slow stack as a list
slow: Noun,
/// Mean stack as a list
mean: Noun,
/// Code table for current arm
pile: Noun,
/// Continuation label when returning to this frame
cont: Noun,
/// Result register when returning to this frame
salt: usize,
/// number of locals
vars: usize,
}
impl Frame {
fn init(&mut self, vars: usize, prev: Option<&Frame>) {
*self = Frame {
slow: prev.map_or(D(0), |f| f.slow),
mean: prev.map_or(D(0), |f| f.mean),
pile: NOUN_NONE,
cont: NOUN_NONE,
salt: usize::MAX,
vars,
};
unsafe { write_bytes((self as *mut Frame).add(1) as *mut u64, 0, vars) };
}
unsafe fn current<'a>(stack: &NockStack) -> &'a Self {
&(*(stack.get_frame_base() as *const Frame))
}
unsafe fn current_mut<'a>(stack: &NockStack) -> &'a mut Self {
&mut (*(stack.get_frame_base() as *mut Frame))
}
fn vars<'a>(&self) -> &'a [Noun] {
unsafe { from_raw_parts((self as *const Frame).add(1) as *const Noun, self.vars) }
}
fn vars_mut<'a>(&mut self) -> &'a mut [Noun] {
unsafe { from_raw_parts_mut((self as *mut Frame).add(1) as *mut Noun, self.vars) }
}
fn mean_push(&mut self, stack: &mut NockStack, entry: Noun) {
self.mean = T(stack, &[entry, self.mean]);
}
fn mean_pop(&mut self) {
self.mean = self
.mean
.as_cell()
.expect("Cannot pop empty mean stack")
.tail();
}
fn slow_push(&mut self, stack: &mut NockStack, entry: Noun) {
self.slow = T(stack, &[entry, self.slow]);
}
fn slow_pop(&mut self) {
self.slow = self
.slow
.as_cell()
.expect("Cannot pop empty slow stack")
.tail();
}
}
assert_eq_align!(Frame, u64, usize);
assert_eq_size!(u64, usize);
const FRAME_WORD_SIZE: usize = size_of::<Frame>() / size_of::<u64>();
fn push_interpreter_frame(stack: &mut NockStack, pile: Noun) {
let vars = pile_sans(pile);
let prev = unsafe { Frame::current(stack) };
stack.frame_push(FRAME_WORD_SIZE + vars);
let frame = unsafe { Frame::current_mut(stack) };
frame.init(vars, Some(prev));
frame.pile = pile;
}
fn push_outer_frame(stack: &mut NockStack, pile: Noun) {
let vars = pile_sans(pile);
stack.frame_push(FRAME_WORD_SIZE + vars);
let frame = unsafe { Frame::current_mut(stack) };
frame.init(vars, None);
frame.pile = pile;
}
const PEEK_AXIS: u64 = 4;
const POKE_AXIS: u64 = 46;
fn slam_line(context: &mut Context, arm_axis: u64, sample: Noun) -> Noun {
let axis_noun = DirectAtom::new_panic(arm_axis).as_noun();
let subject = T(&mut context.stack, &[sample, context.line]);
let sample_patch = T(&mut context.stack, &[D(6), D(0), D(2)]);
let arm_kick_form = T(&mut context.stack, &[D(9), axis_noun, D(0), D(3)]);
let gate_slam_form = T(
&mut context.stack,
&[D(9), D(2), D(10), sample_patch, arm_kick_form],
);
interpret(context, subject, gate_slam_form).expect("Crash in codegen")
}
fn cg_peek(context: &mut Context, subject: Noun, formula: Noun) -> Option<Noun> {
assert!(!context.line.is_none());
let sample = T(&mut context.stack, &[subject, formula]);
let peek_result = slam_line(context, PEEK_AXIS, sample);
if unsafe { peek_result.raw_equals(D(0)) } {
None
} else {
let unit_cell = peek_result.as_cell().expect("Peek should return unit");
Some(unit_cell.tail())
}
}
fn cg_poke(context: &mut Context, slow: Noun, subject: Noun, formula: Noun) {
assert!(!context.line.is_none());
let sample = T(
&mut context.stack,
&[D(tas!(b"comp")), slow, subject, formula],
);
let result = slam_line(context, POKE_AXIS, sample);
let new_line = slot(result, 7).expect("Poke should return triple");
context.line = new_line;
}
/// Get the $pile for an arm, possibly updating the line core
fn cg_indirect(
context: &mut Context,
hill: &mut Noun,
slow: Noun,
subject: Noun,
formula: Noun,
) -> Noun {
let bell_hill = if let Some(res) = cg_peek(context, subject, formula) {
res
} else {
cg_poke(context, slow, subject, formula);
cg_peek(context, subject, formula).expect("Codegen peek should return value after poke.")
};
let bell_hill_cell = bell_hill
.as_cell()
.expect("Codegen successful peek should return pair");
get_by(
&mut context.stack,
&mut bell_hill_cell.tail(),
&mut bell_hill_cell.head(),
)
.expect("Codegen bell lookup should succeed.")
.expect("Codegen peek bell should be in hill")
}
pub fn cg_interpret(context: &mut Context, slow: Noun, subject: Noun, formula: Noun) -> Result {
let mut hill = NOUN_NONE;
let outer_pile = cg_indirect(context, &mut hill, slow, subject, formula);
let virtual_frame = context.stack.get_frame_pointer();
push_outer_frame(&mut context.stack, outer_pile);
let mut wish = pile_wish(outer_pile);
let (mut body, mut bend) = get_blob(context, outer_pile, &mut wish);
let sire = pile_sire(outer_pile);
(unsafe { Frame::current_mut(&context.stack).vars_mut() })[sire] = subject;
let inner_res = 'interpret: loop {
let frame = unsafe { Frame::current_mut(&context.stack) };
if let Ok(body_cell) = body.as_cell() {
body = body_cell.tail();
let inst_cell = body_cell
.head()
.as_cell()
.expect("Codegen instruction should be a cell");
let inst_tag = inst_cell
.head()
.as_atom()
.expect("Codegen instruction tag should be atom")
.as_u64()
.expect("codegen instruction tag should convert to u64");
match inst_tag {
tas!(b"imm") => {
let imm_cell = inst_cell.tail().as_cell().unwrap();
let imm_n = imm_cell.head();
let imm_d = imm_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
frame.vars_mut()[imm_d] = imm_n;
}
tas!(b"mov") => {
let mov_cell = inst_cell.tail().as_cell().unwrap();
let mov_s = mov_cell.head().as_atom().unwrap().as_u64().unwrap() as usize;
let mov_d = mov_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
frame.vars_mut()[mov_d] = frame.vars()[mov_s];
}
tas!(b"inc") => {
let inc_cell = inst_cell.tail().as_cell().unwrap();
let inc_s = inc_cell.head().as_atom().unwrap().as_u64().unwrap() as usize;
let inc_d = inc_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
if let Ok(s_atom) = frame.vars()[inc_s].as_atom() {
frame.vars_mut()[inc_d] = inc(&mut context.stack, s_atom).as_noun();
} else {
break BAIL_EXIT;
}
}
tas!(b"con") => {
let con_cell = inst_cell.tail().as_cell().unwrap();
let con_h = con_cell.head().as_atom().unwrap().as_u64().unwrap() as usize;
let con_tell = con_cell.tail().as_cell().unwrap();
let con_t = con_tell.head().as_atom().unwrap().as_u64().unwrap() as usize;
let con_d = con_tell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
frame.vars_mut()[con_d] = T(
&mut context.stack,
&[frame.vars()[con_h], frame.vars()[con_t]],
);
}
tas!(b"hed") => {
let hed_cell = inst_cell.tail().as_cell().unwrap();
let hed_s = hed_cell.head().as_atom().unwrap().as_u64().unwrap() as usize;
let hed_d = hed_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
let s_noun = frame.vars()[hed_s];
if s_noun.is_none() {
frame.vars_mut()[hed_d] = NOUN_NONE;
} else if let Ok(s_cell) = frame.vars()[hed_s].as_cell() {
frame.vars_mut()[hed_d] = s_cell.head();
} else {
frame.vars_mut()[hed_d] = NOUN_NONE;
}
}
tas!(b"tal") => {
let tal_cell = inst_cell.tail().as_cell().unwrap();
let tal_s = tal_cell.head().as_atom().unwrap().as_u64().unwrap() as usize;
let tal_d = tal_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
let s_noun = frame.vars()[tal_s];
if s_noun.is_none() {
frame.vars_mut()[tal_d] = NOUN_NONE;
} else if let Ok(s_cell) = frame.vars()[tal_s].as_cell() {
frame.vars_mut()[tal_d] = s_cell.tail();
} else {
frame.vars_mut()[tal_d] = NOUN_NONE;
}
}
tas!(b"men") => {
let men_cell = inst_cell.tail().as_cell().unwrap();
let men_l = men_cell.head();
assert!(men_l.is_atom());
let men_s = men_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
let men_entry = T(&mut context.stack, &[men_l, frame.vars()[men_s]]);
frame.mean = T(&mut context.stack, &[men_entry, frame.mean])
}
tas!(b"man") => {
frame.mean = frame.mean.as_cell().unwrap().tail();
}
tas!(b"slo") => {
let slo_s = inst_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
let slo_tag = frame.vars()[slo_s];
assert!(slo_tag.is_atom());
frame.slow = T(&mut context.stack, &[slo_tag, frame.slow]);
}
tas!(b"sld") => {
frame.slow = frame.slow.as_cell().unwrap().tail();
todo!("sld")
}
tas!(b"hit") => {
// XX TODO implement
}
tas!(b"slg") => {
let slg_s = inst_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
context
.newt
.slog(&mut context.stack, 0, frame.vars()[slg_s]);
}
tas!(b"mew") => {
// XX TODO implement
}
tas!(b"tim") => {
// XX TODO implement
}
tas!(b"tom") => {
// XX TODO implement
}
tas!(b"mem") => {
// XX TODO implement
}
tas!(b"poi") => {
let poi_p = inst_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize;
frame.vars_mut()[poi_p] = NOUN_NONE;
}
tas!(b"ipb") => {
let mut ipb_p = inst_cell.tail();
'ipb: loop {
if unsafe { ipb_p.raw_equals(D(0)) } {
break 'ipb;
} else {
let p_cell = ipb_p.as_cell().unwrap();
ipb_p = p_cell.tail();
let p_i = p_cell.head().as_atom().unwrap().as_u64().unwrap() as usize;
if frame.vars()[p_i].is_none() {
break 'interpret BAIL_EXIT;
}
}
}
}
_ => {
panic!("Codegen instruction unsupported");
}
}
} else {
let inst_cell = bend
.as_cell()
.expect("Codegen instruction should be a cell");
let inst_tag = inst_cell
.head()
.as_atom()
.expect("Codegen instruction tag should be atom")
.as_u64()
.expect("codegen instruction tag should convert to u64");
match inst_tag {
tas!(b"clq") => {
todo!("clq")
}
tas!(b"eqq") => {
todo!("eqq")
}
tas!(b"brn") => {
todo!("brn")
}
tas!(b"hop") => {
todo!("hop")
}
tas!(b"hip") => {
todo!("hip")
}
tas!(b"lnk") => {
todo!("lnk")
}
tas!(b"cal") => {
todo!("cal")
}
tas!(b"caf") => {
todo!("caf")
}
tas!(b"lnt") => {
todo!("lnt")
}
tas!(b"jmp") => {
todo!("jmp")
}
tas!(b"jmf") => {
todo!("jmf")
}
tas!(b"spy") => {
todo!("spy")
}
tas!(b"mer") => {
todo!("mer")
}
tas!(b"don") => {
todo!("don")
}
tas!(b"bom") => {
todo!("bom")
}
_ => {
panic!("Codegen instruction unsupported");
}
}
}
};
match inner_res {
Ok(res) => inner_res,
Err(err) => exit(context, err),
}
}
/// Crash with an
fn exit(context: &mut Context, err: Error) -> Result {
todo!("exit")
}
fn pile_sans(pile: Noun) -> usize {
(slot(pile, 127)
.expect("Codegen pile should have sans face")
.as_atom()
.expect("Codegen sans should be atom")
.as_u64()
.expect("Codegen sans too big")) as usize
}
fn pile_wish(pile: Noun) -> Noun {
slot(pile, 30).expect("Codegen pile should have wish face")
}
fn pile_sire(pile: Noun) -> usize {
(slot(pile, 62)
.expect("Codegen pile should have sire face")
.as_atom()
.expect("Codegen sire should be atom")
.as_u64()
.expect("Codegen sire too big")) as usize
}
fn pile_will(pile: Noun) -> Noun {
slot(pile, 126).expect("Codegen pile should have will face")
}
fn get_blob(context: &mut Context, pile: Noun, bile: &mut Noun) -> (Noun, Noun) {
let mut will = pile_will(pile);
let blob_with_biff = get_by(&mut context.stack, &mut will, bile)
.expect("Codegen bile lookup successful")
.expect("Codegen will has bile");
let blob_cell = slot(blob_with_biff, 3)
.expect("Codegen blob has tail")
.as_cell()
.expect("Codegen blob tail should be cell");
(blob_cell.head(), blob_cell.tail())
}

View File

@ -268,6 +268,7 @@ pub struct Context {
pub cold: Cold,
pub warm: Warm,
pub hot: Hot,
pub line: Noun,
pub cache: Hamt<Noun>,
pub scry_stack: Noun,
pub trace_info: Option<TraceInfo>,
@ -360,7 +361,7 @@ impl From<cold::Error> for Error {
pub type Result = result::Result<Noun, Error>;
const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0)));
pub const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0)));
const BAIL_FAIL: Result = Err(Error::NonDeterministic(Mote::Fail, D(0)));
const BAIL_INTR: Result = Err(Error::NonDeterministic(Mote::Intr, D(0)));

View File

@ -310,7 +310,7 @@ pub mod util {
use super::*;
use crate::hamt::Hamt;
use crate::mem::NockStack;
use crate::noun::{Atom, Noun, D, T};
use crate::noun::{Atom, Noun, D, NOUN_NONE, T};
use crate::unifying_equality::unifying_equality;
use assert_no_alloc::assert_no_alloc;
use ibig::UBig;
@ -322,6 +322,7 @@ pub mod util {
let warm = Warm::new(&mut stack);
let hot = Hot::init(&mut stack, URBIT_HOT_STATE);
let cache = Hamt::<Noun>::new(&mut stack);
let line = NOUN_NONE;
Context {
stack,
@ -330,6 +331,7 @@ pub mod util {
warm,
hot,
cache,
line,
scry_stack: D(0),
trace_info: None,
}

View File

@ -5,7 +5,6 @@
// use crate::jets::util::*;
// use crate::jets::Result;
// use crate::noun::{IndirectAtom, Noun, D};
use self::util::*;
crate::gdb!();
@ -13,14 +12,14 @@ crate::gdb!();
// XX TODO actual jets
pub mod util {
use crate::mug::mug_u32;
use crate::unifying_equality::unifying_equality;
use crate::mem::NockStack;
use crate::jets::math::util::lth_b;
use crate::jets::util::slot;
use crate::noun::{Noun, D};
use either::Either::*;
use crate::jets::JetErr;
use crate::mem::NockStack;
use crate::mug::mug_u32;
use crate::noun::{Noun, D};
use crate::unifying_equality::unifying_equality;
use either::Either::*;
pub fn dor_b(stack: &mut NockStack, a: &mut Noun, b: &mut Noun) -> bool {
let mut ap = a as *mut Noun;
@ -38,10 +37,14 @@ pub mod util {
} else {
break true;
}
},
}
Right(a_cell) => {
if let Ok(b_cell) = (*bp).as_cell() {
if unifying_equality(stack, a_cell.head_as_mut(), b_cell.head_as_mut()) {
if unifying_equality(
stack,
a_cell.head_as_mut(),
b_cell.head_as_mut(),
) {
ap = a_cell.tail_as_mut();
bp = b_cell.tail_as_mut();
continue;
@ -70,7 +73,11 @@ pub mod util {
}
}
pub fn get_by(stack: &mut NockStack, a: &mut Noun, b: &mut Noun) -> Result<Option<Noun>, JetErr> {
pub fn get_by(
stack: &mut NockStack,
a: &mut Noun,
b: &mut Noun,
) -> Result<Option<Noun>, JetErr> {
let mut ap = a as *mut Noun;
let bp = b as *mut Noun;
unsafe {

View File

@ -3,6 +3,7 @@ extern crate num_derive;
extern crate lazy_static;
#[macro_use]
extern crate static_assertions;
pub mod codegen;
pub mod flog;
pub mod guard;
pub mod hamt;

View File

@ -9,7 +9,7 @@ use memmap::MmapMut;
use std::alloc::Layout;
use std::mem;
use std::ptr;
use std::ptr::copy_nonoverlapping;
use std::ptr::{copy, copy_nonoverlapping};
crate::gdb!();
@ -146,6 +146,45 @@ impl NockStack {
self.frame_pointer
}
pub fn get_frame_base(&self) -> *mut u64 {
if self.is_west() {
unsafe { *(self.prev_alloc_pointer_pointer()) }
} else {
unsafe { self.frame_pointer.add(RESERVED) }
}
}
pub unsafe fn resize_frame(&mut self, new_size: usize) {
// lightweight stack must be empty
assert!(self.stack_pointer == self.frame_pointer);
let raw_new_size = (new_size + RESERVED) as isize;
if self.is_west() {
let current_size = self
.frame_pointer
.offset_from(*(self.prev_alloc_pointer_pointer()));
assert!(current_size >= RESERVED as isize);
let offset = raw_new_size - current_size;
let new_frame_pointer = self.frame_pointer.offset(offset);
copy(
self.frame_pointer.sub(RESERVED),
new_frame_pointer.sub(RESERVED),
RESERVED,
);
self.frame_pointer = new_frame_pointer;
self.stack_pointer = new_frame_pointer;
} else {
let current_size =
(*(self.prev_alloc_pointer_pointer())).offset_from(self.frame_pointer);
assert!(current_size >= RESERVED as isize);
let offset = current_size - raw_new_size;
let new_frame_pointer = self.frame_pointer.offset(offset);
let copy_size = current_size.min(raw_new_size);
copy(self.frame_pointer, new_frame_pointer, RESERVED);
self.frame_pointer = new_frame_pointer;
self.stack_pointer = new_frame_pointer;
}
}
/** Current stack pointer of this NockStack */
pub fn get_stack_pointer(&self) -> *const u64 {
self.stack_pointer

View File

@ -31,6 +31,9 @@ const CELL_TAG: u64 = u64::MAX & INDIRECT_MASK;
/** Tag mask for a cell. */
const CELL_MASK: u64 = !(u64::MAX >> 3);
const NONE_BITS: u64 = !(u64::MAX >> 3);
pub const NOUN_NONE: Noun = Noun { raw: NONE_BITS };
/* A note on forwarding pointers:
*
* Forwarding pointers are only used temporarily during copies between NockStack frames and between
@ -155,6 +158,10 @@ fn is_cell(noun: u64) -> bool {
noun & CELL_MASK == CELL_TAG
}
fn is_none(noun: u64) -> bool {
noun == NONE_BITS
}
/** A noun-related error. */
#[derive(Debug, PartialEq)]
pub enum Error {
@ -1023,14 +1030,16 @@ pub union Noun {
impl Noun {
pub fn is_none(self) -> bool {
unsafe { self.raw == u64::MAX }
unsafe { is_none(self.raw) }
}
pub fn is_direct(&self) -> bool {
assert!(!self.is_none());
unsafe { is_direct_atom(self.raw) }
}
pub fn is_indirect(&self) -> bool {
assert!(!self.is_none());
unsafe { is_indirect_atom(self.raw) }
}
@ -1043,6 +1052,7 @@ impl Noun {
}
pub fn is_cell(&self) -> bool {
assert!(!self.is_none());
unsafe { is_cell(self.raw) }
}

View File

@ -8,7 +8,7 @@ use crate::jets::warm::Warm;
use crate::mem::NockStack;
use crate::mug::*;
use crate::newt::Newt;
use crate::noun::{Atom, Cell, DirectAtom, Noun, Slots, D, T};
use crate::noun::{Atom, Cell, DirectAtom, Noun, Slots, D, NOUN_NONE, T};
use crate::persist::pma_meta_set;
use crate::persist::{pma_meta_get, pma_open, pma_sync, Persist};
use crate::trace::*;
@ -172,6 +172,7 @@ impl Context {
cache,
scry_stack: D(0),
trace_info,
line: NOUN_NONE,
};
Context {