]> Witch of Git - ivy/blob - rt/src/lib.rs
Stop with #![no_std] in the runtime
[ivy] / rt / src / lib.rs
1 use std::sync::atomic::{Ordering, AtomicU32};
2
3 const _STDOUT: i32 = 1;
4 const STDERR: i32 = 2;
5
6 macro_rules! trace {
7 ($fmt:literal $(, $arg:expr)* $(,)?) => {
8 if std::env::var("IVY_RT_TRACE").is_ok() {
9 eprintln!($fmt, $($arg),*);
10 }
11 }
12 }
13
14 #[repr(u8)]
15 #[derive(PartialEq, Eq)]
16 pub enum ObjTag {
17 Lam = 0,
18 Int = 1,
19 }
20
21 #[repr(C)]
22 pub struct ObjHeader {
23 tag: ObjTag,
24 rc: AtomicU32,
25 }
26
27 #[repr(C)]
28 pub struct ObjInt {
29 tag: ObjTag,
30 rc: AtomicU32,
31 value: i64,
32 }
33
34 #[repr(C)]
35 pub struct ObjLam {
36 tag: ObjTag,
37 upvars: u16,
38 rc: AtomicU32,
39 func: fn(&ObjLam) -> Obj,
40 params: u16,
41 filled: u16,
42 }
43
44 #[derive(Clone, Copy)]
45 pub union Obj {
46 int: i64,
47 header: *mut ObjHeader,
48 box_lam: *mut ObjLam,
49 }
50
51 mod sys {
52 extern "C" {
53 pub fn write(fd: i32, buf: *const u8, len: usize) -> isize;
54 pub fn exit(code: i32) -> !;
55 pub fn malloc(size: usize) -> *mut u8;
56 pub fn free(ptr: *mut u8);
57 }
58 }
59
60 #[no_mangle]
61 pub unsafe extern "C" fn ivy_debug(obj: Obj) -> Obj {
62 println!("DEBUG {:016x}", obj.int);
63 obj
64 }
65
66 #[no_mangle]
67 pub unsafe extern "C" fn ivy_abort(msg: *const u8, len: usize) -> ! {
68 sys::write(STDERR, msg, len);
69 sys::exit(1);
70 }
71
72 #[no_mangle]
73 pub unsafe extern "C" fn ivy_exit(code: i32) -> ! {
74 sys::exit(code)
75 }
76
77 #[no_mangle]
78 pub unsafe extern "C" fn ivy_check_int(obj: Obj) {
79 if !obj.is_int() {
80 panic!("ivy_check_int called with non-integer object {:016x}.", obj.int);
81 }
82 }
83
84 #[no_mangle]
85 pub unsafe extern "C" fn ivy_check_lam(obj: Obj) {
86 if !obj.is_lam() {
87 panic!("ivy_check_lam called with non-lambda object {:016x}.", obj.int);
88 }
89 }
90
91 // This should probably be a macro rather than a call?
92 // But it might be good to have it for completeness.
93 // Or maybe it's valuable if we want to support big integers.
94 #[no_mangle]
95 pub unsafe extern "C" fn ivy_make_int(value: i64) -> Obj {
96 Obj { int: value << 1 }
97 }
98
99 #[no_mangle]
100 pub unsafe extern "C" fn ivy_make_lam(func: fn(&ObjLam) -> Obj, params: u16, upvars: u16) -> Obj {
101 let size = ObjLam::size_of(params, upvars);
102 let box_lam = sys::malloc(size) as *mut ObjLam;
103 box_lam.write(ObjLam {
104 tag: ObjTag::Lam,
105 upvars,
106 rc: AtomicU32::new(0),
107 func,
108 params,
109 filled: 0,
110 });
111 (*box_lam)
112 .raw_fields_mut()
113 .write_bytes(0, (params + upvars) as usize);
114 trace!("MAKE {:016x} {:016x}", box_lam as usize, func as usize);
115 Obj { box_lam }
116 }
117
118 #[no_mangle]
119 pub unsafe extern "C" fn ivy_free(obj: Obj) {
120 if !obj.is_box() {
121 return;
122 }
123 sys::free(obj.header as *mut u8)
124 }
125
126 #[no_mangle]
127 pub unsafe extern "C" fn ivy_incref(obj: Obj) {
128 obj.incref();
129 }
130
131 #[no_mangle]
132 pub unsafe extern "C" fn ivy_decref(obj: Obj) {
133 obj.decref();
134 }
135
136 #[no_mangle]
137 pub unsafe extern "C" fn ivy_clone(obj: Obj) -> Obj {
138 if obj.is_null() || !obj.is_box() {
139 return obj;
140 }
141 if obj.is_int() {
142 unimplemented!("copying boxed integers")
143 }
144 let lam = &*obj.box_lam;
145 let size = lam.size();
146 let data = sys::malloc(size);
147 core::ptr::copy(obj.box_lam as *const u8, data, size);
148 let box_lam = data as *mut ObjLam;
149 *(*box_lam).rc.get_mut() = 0;
150 trace!("COPY {:016x} {:016x}", obj.int, box_lam as usize);
151 Obj { box_lam }
152 }
153
154 #[no_mangle]
155 pub unsafe extern "C" fn ivy_app(fun: Obj, arg: Obj) -> Obj {
156 ivy_app_mut(ivy_clone(fun), arg)
157 }
158
159 #[no_mangle]
160 pub unsafe extern "C" fn ivy_app_mut(fun: Obj, arg: Obj) -> Obj {
161 trace!("APP {:016x} {:016x}", fun.int, arg.int);
162 if !fun.is_lam() {
163 panic!("ivy_app called with a non-lam as the function: {:016x}.", fun.int);
164 }
165 let lam = &mut *fun.box_lam;
166 if lam.filled < lam.params {
167 if arg.is_null() {
168 println!(
169 "Lam @ {:016x} ({:016x}) has {} of {} arguments filled.",
170 fun.int, lam.func as usize, lam.filled, lam.params
171 );
172 panic!("ivy_app called with a null arg.");
173 }
174 arg.incref();
175 let idx = lam.filled as usize;
176 lam.params_mut()[idx] = arg;
177 lam.filled += 1;
178 } else if lam.params == lam.filled {
179 if !arg.is_null() {
180 panic!("ivy_app called for a 0-arity application with a non-null arg.");
181 }
182 }
183
184 if lam.params == lam.filled {
185 trace!("RUN {:016x}", fun.int);
186 (lam.func)(lam)
187 } else {
188 trace!("UPD8 {:016x}", fun.int);
189 fun.incref();
190 fun
191 }
192 }
193
194 impl Obj {
195 fn is_null(self) -> bool {
196 unsafe { self.int == 0 }
197 }
198
199 fn is_box(self) -> bool {
200 !self.is_null() && unsafe { self.int & 1 == 0 }
201 }
202
203 unsafe fn is_int(self) -> bool {
204 !self.is_null() && (!self.is_box() || (*self.header).tag == ObjTag::Int)
205 }
206
207 unsafe fn is_lam(self) -> bool {
208 self.is_box() && (*self.header).tag == ObjTag::Lam
209 }
210
211 unsafe fn incref(self) {
212 if !self.is_box() {
213 return;
214 }
215 (*self.header).rc.fetch_add(1, Ordering::SeqCst);
216 }
217
218 unsafe fn decref(self) {
219 if !self.is_box() {
220 return;
221 }
222 if (*self.header).rc.fetch_sub(1, Ordering::SeqCst) == 1 {
223 self.dealloc();
224 }
225 }
226
227 unsafe fn dealloc(self) {
228 if !self.is_box() {
229 return;
230 }
231 if self.is_lam() {
232 let lam = &mut *self.box_lam;
233 for param in lam.params_mut() {
234 param.decref();
235 }
236 for upvar in lam.upvars_mut() {
237 upvar.decref();
238 }
239 }
240 sys::free(self.header as *mut u8);
241 }
242 }
243
244 impl ObjLam {
245 fn size_of(params: u16, upvars: u16) -> usize {
246 core::mem::size_of::<ObjLam>() + params as usize * 8 + upvars as usize * 8
247 }
248
249 fn size(&self) -> usize {
250 ObjLam::size_of(self.params, self.upvars)
251 }
252
253 unsafe fn raw_fields_mut(&mut self) -> *mut Obj {
254 (self as *mut ObjLam).add(1) as *mut Obj
255 }
256
257 unsafe fn params_mut(&mut self) -> &mut [Obj] {
258 let ptr = self.raw_fields_mut();
259 core::slice::from_raw_parts_mut(ptr, self.params as usize)
260 }
261
262 unsafe fn upvars_mut(&mut self) -> &mut [Obj] {
263 let ptr = self.raw_fields_mut().add(self.params as usize);
264 core::slice::from_raw_parts_mut(ptr, self.upvars as usize)
265 }
266 }