Skip to content

Commit

Permalink
Merge branch 'lpereira-main'
Browse files Browse the repository at this point in the history
  • Loading branch information
dannyvankooten committed May 29, 2024
2 parents c050d62 + e111b41 commit 655745f
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 71 deletions.
40 changes: 40 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ edition = "2021"
[features]
debug = []

[dependencies]

[dev-dependencies]
criterion = {version = "0.3", default-features = false }

Expand All @@ -29,4 +27,7 @@ harness = false

[[bench]]
name = "bench_arithmetic"
harness = false
harness = false

[dependencies]
bitvec = "1.0.1"
106 changes: 62 additions & 44 deletions src/gc.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
use crate::object::{Header, Object, Type};
use crate::object::{Object, Type};
use bitvec::prelude as bv;

// TODO: Change visibility of GC to crate-private (not directly possible because of pub Object type)
pub struct GC {
/// Vector of all currently alive heap-allocated objects in the universe
objects: Vec<Object>,

/// All marked objects during a run.
mark_bitmap: bv::BitVec,
}

impl GC {
/// Create a new Garbage Collector to manage heap-allocated objects
pub fn new() -> GC {
Self {
objects: Vec::new(),
mark_bitmap: bv::BitVec::new(),
}
}

#[inline]
pub fn maybe_trace(&mut self, o: Object) {
if o.is_heap_allocated() {
self.objects.push(o);
self.mark_bitmap.reserve(1);
}
}

/// Adds the given object to the list of objects to manage
#[inline]
pub fn trace(&mut self, o: Object) {
self.objects.push(o);
self.mark_bitmap.reserve(1);
}

/// Removes the given object (and everything it refers) from this garbage collector so it is no longer managed by it
Expand All @@ -44,6 +51,8 @@ impl GC {
}
}
}

self.mark_bitmap.truncate(self.objects.len());
}
}

Expand All @@ -55,47 +64,73 @@ impl GC {

/// Runs a full mark & sweep cycle
/// Only objects in the given roots are kept alive
pub fn run(&mut self, roots: &mut [&mut [Object]]) {
pub fn run(&mut self, roots: &[&[Object]]) {
// Don't traverse roots if we have no traced objects
if self.objects.is_empty() {
return;
}

// mark all reachable objects
for root in roots.iter_mut() {
for obj in root.iter_mut() {
mark(obj);
self.mark_bitmap.clear();

// Mark all reachable objects
for root in roots.iter() {
for obj in root.iter() {
self.mark(obj);
}
}

// sweep unmarked objects
// Sweep all unreachable objects
self.sweep();
}

/// Sweep all unmarked objects
pub fn sweep(&mut self) {
let mut i = 0;
while i < self.objects.len() {
let mut obj = self.objects[i];

// Immediate values should not end up on the traced objects list
debug_assert!(obj.is_heap_allocated());

// Object is heap allocated
// Read its header to check if its marked
// If its marked, clear flag & continue
let mut header = unsafe { Header::read(&mut obj) };
if header.marked {
header.marked = false;
i += 1;
continue;
}
// Sweep in reverse unmarked order to preserve the index as
// elements are removed from the objects vector.
for unmarked in self.mark_bitmap.iter_zeros().rev() {
let object = self.objects.swap_remove(unmarked);
debug_assert!(object.is_heap_allocated());
object.free();
}

// Remove object from tracing list
self.objects.swap_remove(i);
self.mark_bitmap.truncate(self.objects.len());
}

// Drop and deallocate object
obj.free();
/// Marks the given object as reachable
#[inline(always)]
fn mark(&mut self, o: &Object) {
if !o.is_heap_allocated() {
return;
}

let index = unsafe {
let object_ptr: *mut Object = o.as_ptr().cast();
let universe_ptr: *const Object = self.objects.as_ptr().cast();
object_ptr.offset_from(universe_ptr) as usize
};
debug_assert!(index < self.objects.len());

if o.tag() == Type::Array {
// Safety: we know the size of mark_bitmap.
unsafe {
// No need to mark recursively on arrays if this one was
// already marked (e.g. because the same object was found
// in multiple places such as the stack and the result of
// a function call).
if !self.mark_bitmap.get_unchecked(index) {
self.mark_bitmap.set_unchecked(index, true);

// Safety: we already checked the type.
for v in o.as_vec_unchecked() {
self.mark(v);
}
}
}
} else {
unsafe {
// Safety: we know the size of mark_bitmap.
self.mark_bitmap.set_unchecked(index, true);
}
}
}
}
Expand All @@ -106,20 +141,3 @@ impl Drop for GC {
self.destroy();
}
}

/// Marks the given object as reachable
#[inline]
fn mark(o: &mut Object) {
if !o.is_heap_allocated() {
return;
}

let mut header = unsafe { Header::read(o) };
header.marked = true;

if o.tag() == Type::Array {
for v in o.as_vec_mut() {
mark(v);
}
}
}
21 changes: 0 additions & 21 deletions src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,21 +426,7 @@ impl Object {
impl_logical!(or, ||);
}

#[repr(C)]
pub(crate) struct Header {
pub(crate) marked: bool,
}

impl Header {
#[inline]
pub unsafe fn read(obj: &mut Object) -> &mut Header {
obj.get_mut::<Self>()
}
}

#[repr(C)]
struct Float {
header: Header,
value: f64,
}

Expand All @@ -459,15 +445,12 @@ impl Float {
fn from_f64(value: f64) -> Object {
let ptr = Object::with_type(allocate(Layout::new::<Self>()), Type::Float);
let obj = unsafe { ptr.get_mut::<Self>() };
obj.header.marked = false;
init!(obj.value => value );
ptr
}
}

#[repr(C)]
struct String {
header: Header,
value: RString,
}

Expand All @@ -480,15 +463,12 @@ impl String {
fn from_string(value: RString) -> Object {
let ptr = Object::with_type(allocate(Layout::new::<Self>()), Type::String);
let obj = unsafe { ptr.get_mut::<Self>() };
obj.header.marked = false;
init!(obj.value => value);
ptr
}
}

#[repr(C)]
struct Array {
header: Header,
value: Vec<Object>,
}

Expand All @@ -506,7 +486,6 @@ impl Array {
fn from_vec(vec: Vec<Object>) -> Object {
let ptr = Object::with_type(allocate(Layout::new::<Self>()), Type::Array);
let obj = unsafe { ptr.get_mut::<Self>() };
obj.header.marked = false;
init!(obj.value => vec);
ptr
}
Expand Down
19 changes: 16 additions & 3 deletions src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,6 @@ impl VM {
OpCode::Jump => {
let pos = self.read_u16();
self.jump(pos);

// collect garbage on every jump instruction
// gc.run(&[&self.stack, &constants, &self.globals, &[final_result]]);
}
OpCode::JumpIfFalse => {
let condition = self.pop();
Expand Down Expand Up @@ -379,10 +376,26 @@ impl VM {
OpCode::ReturnValue => {
let result = self.pop();
self.popframe();

gc.run(&[
self.stack.as_slice(),
constants.as_slice(),
self.globals.as_slice(),
&[final_result, result],
]);

self.push(result);
}
OpCode::Return => {
self.popframe();

gc.run(&[
self.stack.as_slice(),
constants.as_slice(),
self.globals.as_slice(),
&[final_result],
]);

self.push(Object::null());
}
OpCode::GtLocalConst => impl_binary_const_local_op_method!(gt),
Expand Down

0 comments on commit 655745f

Please sign in to comment.