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