From 780dbac037c8c7ece5e97e08efd49b6e479df151 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Sat, 2 Jan 2021 17:12:47 -0500 Subject: [PATCH] [rt] Split the runtime into multiple modules --- rt/src/int.rs | 28 ++++++++++ rt/src/lam.rs | 114 ++++++++++++++++++++++++++++++++++++++ rt/src/lib.rs | 148 ++++---------------------------------------------- rt/src/sys.rs | 6 ++ 4 files changed, 159 insertions(+), 137 deletions(-) create mode 100644 rt/src/int.rs create mode 100644 rt/src/lam.rs create mode 100644 rt/src/sys.rs diff --git a/rt/src/int.rs b/rt/src/int.rs new file mode 100644 index 0000000..6b11d1d --- /dev/null +++ b/rt/src/int.rs @@ -0,0 +1,28 @@ +use crate::{Obj, ObjTag}; +use std::sync::atomic::AtomicU32; + +#[repr(C)] +pub struct ObjInt { + tag: ObjTag, + _pad: [u8; 3], + rc: AtomicU32, + value: i64, +} + +#[no_mangle] +pub unsafe extern "C" fn ivy_check_int(obj: Obj) { + if !obj.is_int() { + panic!( + "ivy_check_int called with non-integer object {:016x}.", + obj.int + ); + } +} + +// This should probably be a macro rather than a call? +// But it might be good to have it for completeness. +// Or maybe it's valuable if we want to support big integers. +#[no_mangle] +pub unsafe extern "C" fn ivy_make_int(value: i64) -> Obj { + Obj { int: value << 1 } +} diff --git a/rt/src/lam.rs b/rt/src/lam.rs new file mode 100644 index 0000000..b998fa8 --- /dev/null +++ b/rt/src/lam.rs @@ -0,0 +1,114 @@ +use crate::{sys, trace, Obj, ObjTag}; +use std::sync::atomic::AtomicU32; + +#[repr(C)] +pub struct ObjLam { + tag: ObjTag, + _pad: [u8; 1], + upvars: u16, + rc: AtomicU32, + func: extern "C" fn(&ObjLam) -> Obj, + params: u16, + filled: u16, +} + +#[no_mangle] +pub unsafe extern "C" fn ivy_check_lam(obj: Obj) { + if !obj.is_lam() { + panic!( + "ivy_check_lam called with non-lambda object {:016x}.", + obj.int + ); + } +} + +#[no_mangle] +pub unsafe extern "C" fn ivy_make_lam( + func: extern "C" fn(&ObjLam) -> Obj, + params: u16, + upvars: u16, +) -> Obj { + let size = ObjLam::size_of(params, upvars); + let box_lam = sys::malloc(size) as *mut ObjLam; + box_lam.write(ObjLam { + tag: ObjTag::Lam, + _pad: [0; 1], + upvars, + rc: AtomicU32::new(0), + func, + params, + filled: 0, + }); + (*box_lam) + .raw_fields_mut() + .write_bytes(0, (params + upvars) as usize); + trace!("MAKE {:016x} {:016x}", box_lam as usize, func as usize); + Obj { box_lam } +} + +#[no_mangle] +pub unsafe extern "C" fn ivy_app(fun: Obj, arg: Obj) -> Obj { + ivy_app_mut(crate::ivy_clone(fun), arg) +} + +#[no_mangle] +pub unsafe extern "C" fn ivy_app_mut(fun: Obj, arg: Obj) -> Obj { + trace!("APP {:016x} {:016x}", fun.int, arg.int); + if !fun.is_lam() { + panic!( + "ivy_app called with a non-lam as the function: {:016x}.", + fun.int + ); + } + let lam = &mut *fun.box_lam; + if lam.filled < lam.params { + if arg.is_null() { + println!( + "Lam @ {:016x} ({:016x}) has {} of {} arguments filled.", + fun.int, lam.func as usize, lam.filled, lam.params + ); + panic!("ivy_app called with a null arg."); + } + arg.incref(); + let idx = lam.filled as usize; + lam.params_mut()[idx] = arg; + lam.filled += 1; + } else if lam.params == lam.filled { + if !arg.is_null() { + panic!("ivy_app called for a 0-arity application with a non-null arg."); + } + } + + if lam.params == lam.filled { + trace!("RUN {:016x}", fun.int); + (lam.func)(lam) + } else { + trace!("UPD8 {:016x}", fun.int); + fun.incref(); + fun + } +} + +impl ObjLam { + pub(crate) fn size_of(params: u16, upvars: u16) -> usize { + core::mem::size_of::() + params as usize * 8 + upvars as usize * 8 + } + + pub(crate) fn size(&self) -> usize { + ObjLam::size_of(self.params, self.upvars) + } + + pub(crate) unsafe fn raw_fields_mut(&mut self) -> *mut Obj { + (self as *mut ObjLam).add(1) as *mut Obj + } + + pub(crate) unsafe fn params_mut(&mut self) -> &mut [Obj] { + let ptr = self.raw_fields_mut(); + core::slice::from_raw_parts_mut(ptr, self.params as usize) + } + + pub(crate) unsafe fn upvars_mut(&mut self) -> &mut [Obj] { + let ptr = self.raw_fields_mut().add(self.params as usize); + core::slice::from_raw_parts_mut(ptr, self.upvars as usize) + } +} diff --git a/rt/src/lib.rs b/rt/src/lib.rs index adc36f6..626a65d 100644 --- a/rt/src/lib.rs +++ b/rt/src/lib.rs @@ -1,9 +1,15 @@ -use std::sync::atomic::{Ordering, AtomicU32}; +use crate::{int::ObjInt, lam::ObjLam}; +use std::sync::atomic::{AtomicU32, Ordering}; + +pub mod int; +pub mod lam; +pub mod sys; const _STDIN: i32 = 0; const _STDOUT: i32 = 1; const STDERR: i32 = 2; +#[macro_export] macro_rules! trace { ($fmt:literal $(, $arg:expr)* $(,)?) => { if std::env::var("IVY_RT_TRACE").is_ok() { @@ -26,40 +32,13 @@ pub struct ObjHeader { rc: AtomicU32, } -#[repr(C)] -pub struct ObjInt { - tag: ObjTag, - _pad: [u8; 3], - rc: AtomicU32, - value: i64, -} - -#[repr(C)] -pub struct ObjLam { - tag: ObjTag, - _pad: [u8; 1], - upvars: u16, - rc: AtomicU32, - func: extern "C" fn(&ObjLam) -> Obj, - params: u16, - filled: u16, -} - #[derive(Clone, Copy)] #[repr(C)] pub union Obj { int: i64, header: *mut ObjHeader, box_lam: *mut ObjLam, -} - -mod sys { - extern "C" { - pub fn write(fd: i32, buf: *const u8, len: usize) -> isize; - pub fn exit(code: i32) -> !; - pub fn malloc(size: usize) -> *mut u8; - pub fn free(ptr: *mut u8); - } + box_int: *mut ObjInt, } #[no_mangle] @@ -79,48 +58,6 @@ pub unsafe extern "C" fn ivy_exit(code: i32) -> ! { sys::exit(code) } -#[no_mangle] -pub unsafe extern "C" fn ivy_check_int(obj: Obj) { - if !obj.is_int() { - panic!("ivy_check_int called with non-integer object {:016x}.", obj.int); - } -} - -#[no_mangle] -pub unsafe extern "C" fn ivy_check_lam(obj: Obj) { - if !obj.is_lam() { - panic!("ivy_check_lam called with non-lambda object {:016x}.", obj.int); - } -} - -// This should probably be a macro rather than a call? -// But it might be good to have it for completeness. -// Or maybe it's valuable if we want to support big integers. -#[no_mangle] -pub unsafe extern "C" fn ivy_make_int(value: i64) -> Obj { - Obj { int: value << 1 } -} - -#[no_mangle] -pub unsafe extern "C" fn ivy_make_lam(func: extern "C" fn(&ObjLam) -> Obj, params: u16, upvars: u16) -> Obj { - let size = ObjLam::size_of(params, upvars); - let box_lam = sys::malloc(size) as *mut ObjLam; - box_lam.write(ObjLam { - tag: ObjTag::Lam, - _pad: [0; 1], - upvars, - rc: AtomicU32::new(0), - func, - params, - filled: 0, - }); - (*box_lam) - .raw_fields_mut() - .write_bytes(0, (params + upvars) as usize); - trace!("MAKE {:016x} {:016x}", box_lam as usize, func as usize); - Obj { box_lam } -} - #[no_mangle] pub unsafe extern "C" fn ivy_free(obj: Obj) { if !obj.is_box() { @@ -151,52 +88,13 @@ pub unsafe extern "C" fn ivy_clone(obj: Obj) -> Obj { let size = lam.size(); let data = sys::malloc(size); core::ptr::copy(obj.box_lam as *const u8, data, size); + let box_hdr = data as *mut ObjHeader; + *(*box_hdr).rc.get_mut() = 0; + trace!("COPY {:016x} {:016x}", obj.int, box_hdr as usize); let box_lam = data as *mut ObjLam; - *(*box_lam).rc.get_mut() = 0; - trace!("COPY {:016x} {:016x}", obj.int, box_lam as usize); Obj { box_lam } } -#[no_mangle] -pub unsafe extern "C" fn ivy_app(fun: Obj, arg: Obj) -> Obj { - ivy_app_mut(ivy_clone(fun), arg) -} - -#[no_mangle] -pub unsafe extern "C" fn ivy_app_mut(fun: Obj, arg: Obj) -> Obj { - trace!("APP {:016x} {:016x}", fun.int, arg.int); - if !fun.is_lam() { - panic!("ivy_app called with a non-lam as the function: {:016x}.", fun.int); - } - let lam = &mut *fun.box_lam; - if lam.filled < lam.params { - if arg.is_null() { - println!( - "Lam @ {:016x} ({:016x}) has {} of {} arguments filled.", - fun.int, lam.func as usize, lam.filled, lam.params - ); - panic!("ivy_app called with a null arg."); - } - arg.incref(); - let idx = lam.filled as usize; - lam.params_mut()[idx] = arg; - lam.filled += 1; - } else if lam.params == lam.filled { - if !arg.is_null() { - panic!("ivy_app called for a 0-arity application with a non-null arg."); - } - } - - if lam.params == lam.filled { - trace!("RUN {:016x}", fun.int); - (lam.func)(lam) - } else { - trace!("UPD8 {:016x}", fun.int); - fun.incref(); - fun - } -} - impl Obj { fn is_null(self) -> bool { unsafe { self.int == 0 } @@ -255,27 +153,3 @@ impl Obj { sys::free(self.header as *mut u8); } } - -impl ObjLam { - fn size_of(params: u16, upvars: u16) -> usize { - core::mem::size_of::() + params as usize * 8 + upvars as usize * 8 - } - - fn size(&self) -> usize { - ObjLam::size_of(self.params, self.upvars) - } - - unsafe fn raw_fields_mut(&mut self) -> *mut Obj { - (self as *mut ObjLam).add(1) as *mut Obj - } - - unsafe fn params_mut(&mut self) -> &mut [Obj] { - let ptr = self.raw_fields_mut(); - core::slice::from_raw_parts_mut(ptr, self.params as usize) - } - - unsafe fn upvars_mut(&mut self) -> &mut [Obj] { - let ptr = self.raw_fields_mut().add(self.params as usize); - core::slice::from_raw_parts_mut(ptr, self.upvars as usize) - } -} diff --git a/rt/src/sys.rs b/rt/src/sys.rs new file mode 100644 index 0000000..96d05be --- /dev/null +++ b/rt/src/sys.rs @@ -0,0 +1,6 @@ +extern "C" { + pub(crate) fn write(fd: i32, buf: *const u8, len: usize) -> isize; + pub(crate) fn exit(code: i32) -> !; + pub(crate) fn malloc(size: usize) -> *mut u8; + pub(crate) fn free(ptr: *mut u8); +} -- 2.43.2