summaryrefslogtreecommitdiffstats
path: root/bytecode/src/heap.rs
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2024-02-04 16:46:20 -0800
committerRose Hogenson <rosehogenson@posteo.net>2024-02-04 16:46:20 -0800
commit17face3633686e374aa0859271ccf462a48e60aa (patch)
tree6a5482717ef1b8194ccf347a6d8af06d664902cd /bytecode/src/heap.rs
parentd940187fd8720e0ab3c00e5a6a7ae8f181c7752d (diff)
downloadsml-17face3633686e374aa0859271ccf462a48e60aa.tar.zst
Rewrite the bytecode interpreter in Rust.
Diffstat (limited to 'bytecode/src/heap.rs')
-rw-r--r--bytecode/src/heap.rs144
1 files changed, 144 insertions, 0 deletions
diff --git a/bytecode/src/heap.rs b/bytecode/src/heap.rs
new file mode 100644
index 0000000..f1b9d81
--- /dev/null
+++ b/bytecode/src/heap.rs
@@ -0,0 +1,144 @@
+use crate::value::Value;
+use std::error::Error;
+
+pub const NUM_LOCALS: usize = 8;
+
+pub struct Heap {
+ pub buf: Vec<u64>,
+ free_ptr: usize,
+ last_live: usize,
+ pub locals: [Value; NUM_LOCALS],
+}
+
+impl Heap {
+ pub fn new() -> Heap {
+ Heap {
+ buf: Vec::new(),
+ free_ptr: 0,
+ last_live: 0,
+ locals: [Value(0); NUM_LOCALS],
+ }
+ }
+
+ fn forwarded(&self, p: usize) -> bool {
+ let Some(q) = Value(self.buf[p]).to_pointer() else {
+ return false;
+ };
+ return (self.free_ptr < self.buf.len() / 2) == (q < self.buf.len() / 2);
+ }
+
+ fn alloc_size(&self, p: usize) -> usize {
+ usize::try_from(self.buf[p - 1] >> 1).expect("using 32 bits in 2024 LULW")
+ }
+
+ fn simple_alloc(&mut self, size: usize) -> usize {
+ self.buf[self.free_ptr] = u64::try_from(size).expect("how even??") << 1;
+ let p = self.free_ptr + 1;
+ self.free_ptr += size + 1;
+ p
+ }
+
+ fn process_gc_value(&mut self, val: Value) -> Value {
+ let Some(p) = val.to_pointer() else {
+ return val;
+ };
+ if self.forwarded(p) {
+ return Value(self.buf[p]);
+ }
+
+ let size = self.alloc_size(p);
+
+ // Allocate space in the new buffer.
+ let q = self.simple_alloc(size);
+
+ // Write forwarding pointer.
+ let first_val = Value(self.buf[p]);
+ self.buf[p] = Value::from_pointer(q).repr();
+
+ // Copy values, recursively modifying pointers.
+ self.buf[q] = self.process_gc_value(first_val).repr();
+ for i in 1..size {
+ self.buf[q + i] = self.process_gc_value(Value(self.buf[p + i])).repr();
+ }
+
+ Value::from_pointer(q)
+ }
+
+ fn collect_garbage(&mut self) {
+ if self.free_ptr < 2 * self.last_live {
+ return;
+ }
+
+ // Swap the heaps.
+ if self.free_ptr < self.buf.len() / 2 {
+ self.free_ptr = self.buf.len() / 2;
+ } else {
+ self.free_ptr = 0;
+ }
+
+ for i in 0..self.locals.len() {
+ self.locals[i] = self.process_gc_value(self.locals[i]);
+ }
+ if self.free_ptr < self.buf.len() / 2 {
+ self.last_live = self.free_ptr;
+ } else {
+ self.last_live = self.free_ptr - self.buf.len() / 2;
+ }
+ }
+
+ fn more_space(&mut self, size_hint: usize) {
+ let current_size = self.buf.len() / 2;
+ let mut new_size = current_size * 2;
+ if new_size == 0 {
+ new_size = 1;
+ }
+ while new_size <= current_size + size_hint {
+ new_size *= 2;
+ }
+
+ let active;
+ if self.free_ptr < current_size {
+ active = &self.buf[..current_size];
+ } else {
+ active = &self.buf[current_size..];
+ self.free_ptr -= current_size;
+ }
+
+ let mut new_buf = Vec::with_capacity(new_size * 2);
+ new_buf.extend_from_slice(active);
+ new_buf.resize(new_size * 2, 0);
+ self.buf = new_buf;
+ }
+
+ pub fn alloc(&mut self, size: i64) -> Result<usize, Box<dyn Error>> {
+ if size <= 0 {
+ return Err(Box::from("alloc of zero size"));
+ }
+ let usize = usize::try_from(size).expect("32 bits in 2024 LULW");
+ self.collect_garbage();
+ if self.free_ptr + usize + 1 >= self.buf.len() / 2 {
+ self.more_space(usize);
+ }
+
+ let p = self.simple_alloc(usize::try_from(size).expect("using 32 bits in 2024 LULW"));
+ for i in 0..usize {
+ self.buf[p + i] = 0;
+ }
+ Ok(p)
+ }
+
+ pub fn peek(&self, p: usize) -> Result<Value, Box<dyn Error>> {
+ if p >= self.buf.len() {
+ return Err(Box::from("peek: out of range"));
+ }
+ Ok(Value(self.buf[p]))
+ }
+
+ pub fn poke(&mut self, p: usize, val: Value) -> Result<(), Box<dyn Error>> {
+ if p >= self.buf.len() {
+ return Err(Box::from("poke: out of range"));
+ }
+ self.buf[p] = val.repr();
+ Ok(())
+ }
+}