]> Witch of Git - ivy/blob - rt/src/lib.rs
Add skeleton of x64 compilation code
[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_exit(code: i32) -> ! {
80 sys::exit(code)
81 }
82
83 #[no_mangle]
84 pub unsafe extern "C" fn ivy_check_int(obj: Obj) {
85 if !obj.is_int() {
86 abort("Abort: ivy_check_int called with non-integer object.\n");
87 }
88 }
89
90 #[no_mangle]
91 pub unsafe extern "C" fn ivy_check_lam(obj: Obj) {
92 if !obj.is_lam() {
93 abort("Abort: ivy_check_lam called with non-lambda object.\n");
94 }
95 }
96
97 // This should probably be a macro rather than a call?
98 // But it might be good to have it for completeness.
99 // Or maybe it's valuable if we want to support big integers.
100 #[no_mangle]
101 pub unsafe extern "C" fn ivy_make_int(value: i64) -> Obj {
102 Obj { int: value << 1 }
103 }
104
105 #[no_mangle]
106 pub unsafe extern "C" fn ivy_make_lam(func: fn(&ObjLam) -> Obj, params: u16, upvars: u16) -> Obj {
107 let size = ObjLam::size_of(params, upvars);
108 let box_lam = sys::malloc(size) as *mut ObjLam;
109 box_lam.write(ObjLam {
110 tag: ObjTag::Lam,
111 upvars,
112 rc: AtomicU32::new(0),
113 func,
114 params,
115 filled: 0,
116 });
117 (*box_lam)
118 .raw_fields_mut()
119 .write_bytes(0, (params + upvars) as usize);
120 // println!("MAKE {:016x}", box_lam as usize);
121 Obj { box_lam }
122 }
123
124 #[no_mangle]
125 pub unsafe extern "C" fn ivy_free(obj: Obj) {
126 if !obj.is_box() {
127 return;
128 }
129 sys::free(obj.header as *mut u8)
130 }
131
132 #[no_mangle]
133 pub unsafe extern "C" fn ivy_incref(obj: Obj) {
134 obj.incref();
135 }
136
137 #[no_mangle]
138 pub unsafe extern "C" fn ivy_decref(obj: Obj) {
139 obj.decref();
140 }
141
142 #[no_mangle]
143 pub unsafe extern "C" fn ivy_clone(obj: Obj) -> Obj {
144 if obj.is_null() || !obj.is_box() {
145 return obj;
146 }
147 if obj.is_int() {
148 abort("Abort: copying boxed integers is not implemented.\n")
149 }
150 let lam = &*obj.box_lam;
151 let size = lam.size();
152 let data = sys::malloc(size);
153 core::ptr::copy(obj.box_lam as *const u8, data, size);
154 let box_lam = data as *mut ObjLam;
155 *(*box_lam).rc.get_mut() = 0;
156 // println!("COPY {:016x} {:016x}", obj.int, box_lam as usize);
157 Obj { box_lam }
158 }
159
160 #[no_mangle]
161 pub unsafe extern "C" fn ivy_app(mut fun: Obj, arg: Obj) -> Obj {
162 ivy_app_mut(ivy_clone(fun), arg)
163 }
164
165 #[no_mangle]
166 pub unsafe extern "C" fn ivy_app_mut(fun: Obj, arg: Obj) -> Obj {
167 // println!("APP {:016x} {:016x}", fun.int, arg.int);
168 if !fun.is_lam() {
169 abort("Abort: ivy_app called with a non-lam as the function.\n");
170 }
171 let lam = &mut *fun.box_lam;
172 if lam.filled < lam.params {
173 if arg.is_null() {
174 abort("Abort: ivy_app called with a null arg.\n");
175 }
176 arg.incref();
177 let idx = lam.filled as usize;
178 lam.params_mut()[idx] = arg;
179 lam.filled += 1;
180 } else if lam.params == lam.filled {
181 if !arg.is_null() {
182 abort("Abort: ivy_app called for a 0-arity application with a non-null arg.\n");
183 }
184 }
185
186 if lam.params == lam.filled {
187 // println!("RUN {:016x}", fun.int);
188 (lam.func)(lam)
189 } else {
190 // println!("UPD8 {:016x}", fun.int);
191 fun.incref();
192 fun
193 }
194 }
195
196 fn abort(msg: &str) -> ! {
197 unsafe { ivy_abort(msg.as_ptr(), msg.len()) }
198 }
199
200 impl Obj {
201 fn is_null(self) -> bool {
202 unsafe { self.int == 0 }
203 }
204
205 fn is_box(self) -> bool {
206 !self.is_null() && unsafe { self.int & 1 == 0 }
207 }
208
209 unsafe fn refcount(self) -> u32 {
210 if self.is_box() {
211 (*self.header).rc.load(Ordering::SeqCst)
212 } else {
213 1
214 }
215 }
216
217 unsafe fn unique(self) -> bool {
218 self.refcount() == 1
219 }
220
221 unsafe fn is_int(self) -> bool {
222 !self.is_null() && (!self.is_box() || (*self.header).tag == ObjTag::Int)
223 }
224
225 unsafe fn is_lam(self) -> bool {
226 self.is_box() && (*self.header).tag == ObjTag::Lam
227 }
228
229 unsafe fn incref(self) {
230 if !self.is_box() {
231 return;
232 }
233 (*self.header).rc.fetch_add(1, Ordering::SeqCst);
234 }
235
236 unsafe fn decref(self) {
237 if !self.is_box() {
238 return;
239 }
240 if (*self.header).rc.fetch_sub(1, Ordering::SeqCst) == 1 {
241 self.dealloc();
242 }
243 }
244
245 unsafe fn dealloc(self) {
246 if !self.is_box() {
247 return;
248 }
249 if self.is_lam() {
250 let lam = &mut *self.box_lam;
251 for param in lam.params_mut() {
252 param.decref();
253 }
254 for upvar in lam.upvars_mut() {
255 upvar.decref();
256 }
257 }
258 sys::free(self.header as *mut u8);
259 }
260 }
261
262 impl ObjLam {
263 fn size_of(params: u16, upvars: u16) -> usize {
264 core::mem::size_of::<ObjLam>() + params as usize * 8 + upvars as usize * 8
265 }
266
267 fn size(&self) -> usize {
268 ObjLam::size_of(self.params, self.upvars)
269 }
270
271 unsafe fn raw_fields_mut(&mut self) -> *mut Obj {
272 (self as *mut ObjLam).add(1) as *mut Obj
273 }
274
275 unsafe fn params_mut(&mut self) -> &mut [Obj] {
276 let ptr = self.raw_fields_mut();
277 core::slice::from_raw_parts_mut(ptr, self.params as usize)
278 }
279
280 unsafe fn upvars_mut(&mut self) -> &mut [Obj] {
281 let ptr = self.raw_fields_mut().add(self.params as usize);
282 core::slice::from_raw_parts_mut(ptr, self.upvars as usize)
283 }
284 }