From 780dbac037c8c7ece5e97e08efd49b6e479df151 Mon Sep 17 00:00:00 2001
From: Cassie Jones <code@witchoflight.com>
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::<ObjLam>() + 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::<ObjLam>() + 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.47.0