Skip to content

Commit

Permalink
First draft new GC feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
gmorpheme committed Jan 21, 2024
1 parent e20c08d commit ad89462
Show file tree
Hide file tree
Showing 14 changed files with 369 additions and 110 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ version = "0.2.0"
authors = ["gmorpheme <[email protected]>"]
edition = "2021"

[unstable]
feature="core_intrinsics"

[build-dependencies]
lalrpop = { version = "0.19.8", features = ["lexer"] }

Expand Down
19 changes: 7 additions & 12 deletions src/eval/machine/cont.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,16 @@ impl GcScannable for Continuation {
marker: &'b mut CollectorHeapView<'a>,
) -> Vec<ScanPtr<'a>> {
let mut grey = vec![];
dbg!("cont");
match self {
Continuation::Branch {
branches,
fallback,
environment,
} => {
if let Some(data) = branches.allocated_data() {
if marker.mark(data) {
for (_tag, branch) in branches.iter() {
marker.mark(*branch);
grey.push(ScanPtr::from_non_null(scope, *branch));
}
if marker.mark_array(branches) {
for (_tag, branch) in branches.iter() {
marker.mark(*branch);
grey.push(ScanPtr::from_non_null(scope, *branch));
}
}

Expand All @@ -136,11 +133,9 @@ impl GcScannable for Continuation {
}
}
Continuation::ApplyTo { args } => {
if let Some(data) = args.allocated_data() {
if marker.mark(data) {
for arg in args.iter() {
grey.push(ScanPtr::new(scope, arg));
}
if marker.mark_array(args) {
for arg in args.iter() {
grey.push(ScanPtr::new(scope, arg));
}
}
}
Expand Down
14 changes: 3 additions & 11 deletions src/eval/machine/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ where

impl<S> StgObject for Closing<S> where S: Copy {}

impl<S: Copy + Default> Default for Closing<S> {
fn default() -> Self {
Self(Default::default(), RefPtr::dangling())
}
}

impl<S> InfoTable for Closing<S>
where
S: Copy,
Expand Down Expand Up @@ -370,11 +364,9 @@ impl GcScannable for EnvFrame {

let bindings = &self.bindings;

if let Some(data) = bindings.allocated_data() {
if marker.mark(data) {
for binding in bindings.iter() {
grey.push(ScanPtr::new(scope, binding));
}
if marker.mark_array(bindings) {
for binding in bindings.iter() {
grey.push(ScanPtr::new(scope, binding));
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/eval/machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ pub fn standard_machine<'a>(
emitter: Box<dyn Emitter + 'a>,
runtime: &'a dyn Runtime,
) -> Result<Machine<'a>, ExecutionError> {
let mut machine = Machine::new(emitter, settings.trace_steps, settings.heap_limit_mib);
let mut machine = Machine::new(
emitter,
settings.trace_steps,
settings.heap_limit_mib,
settings.heap_dump_at_gc,
);
let (root_env, globals, closure) = { machine.mutate(Initialiser { syntax, runtime }, ())? };

machine.initialise(root_env, globals, closure, runtime.intrinsics())?;
Expand Down
45 changes: 37 additions & 8 deletions src/eval/machine/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,14 +621,16 @@ impl GcScannable for MachineState {
) -> Vec<ScanPtr<'a>> {
let mut grey = vec![];

marker.mark(self.globals);
grey.push(ScanPtr::from_non_null(scope, self.globals));
if marker.mark(self.globals) {
grey.push(ScanPtr::from_non_null(scope, self.globals));
}

grey.push(ScanPtr::new(scope, &self.closure));

for cont in &self.stack {
marker.mark(*cont);
grey.push(ScanPtr::from_non_null(scope, *cont));
if marker.mark(*cont) {
grey.push(ScanPtr::from_non_null(scope, *cont));
}
}

grey
Expand All @@ -637,6 +639,7 @@ impl GcScannable for MachineState {

pub struct MachineSettings {
pub trace_steps: bool,
pub dump_heap: bool,
}

/// An STG machine variant using cactus environment
Expand Down Expand Up @@ -668,13 +671,19 @@ impl<'a> Machine<'a> {
emitter: Box<dyn Emitter + 'a>,
trace_steps: bool,
heap_limit_mib: Option<usize>,
dump_heap: bool,
) -> Self {
Machine {
heap: Heap::with_limit(heap_limit_mib.unwrap_or(256)),
heap: heap_limit_mib
.map(Heap::with_limit)
.unwrap_or_else(|| Heap::new()),
state: Default::default(),
intrinsics: vec![],
emitter,
settings: MachineSettings { trace_steps },
settings: MachineSettings {
trace_steps,
dump_heap,
},
metrics: Metrics::default(),
clock: Clock::default(),
}
Expand Down Expand Up @@ -789,16 +798,36 @@ impl<'a> Machine<'a> {
pub fn run(&mut self, limit: Option<usize>) -> Result<Option<u8>, ExecutionError> {
self.clock.switch(ThreadOccupation::Mutator);

let gc_check_freq = 500;

while !self.state.terminated {
if let Some(limit) = limit {
if self.metrics.ticks() as usize >= limit {
return Err(ExecutionError::DidntTerminate(limit));
}
}

if self.metrics.ticks() % gc_check_freq == 0 {

Check failure on line 810 in src/eval/machine/vm.rs

View workflow job for this annotation

GitHub Actions / Clippy

this `if` statement can be collapsed
if self.heap().policy_requires_collection() {
collect::collect(
&self.state,
&mut self.heap,
&mut self.clock,
self.settings.dump_heap,
);
self.clock.switch(ThreadOccupation::Mutator);
}
}

self.step()?;
}

collect::collect(&self.state, &mut self.heap, &mut self.clock);
collect::collect(
&self.state,
&mut self.heap,
&mut self.clock,
self.settings.dump_heap,
);

self.clock.stop();

Expand Down Expand Up @@ -990,7 +1019,7 @@ pub mod tests {
}

fn machine(syn: Rc<StgSyn>) -> Machine<'static> {
let mut m = Machine::new(Box::new(DebugEmitter::default()), true);
let mut m = Machine::new(Box::new(DebugEmitter::default()), true, None, false);
let blank = m.mutate(Init, ()).unwrap();
let closure = m.mutate(Load { syntax: syn }, blank).unwrap();
m.initialise(blank, blank, closure, vec![]).unwrap();
Expand Down
6 changes: 4 additions & 2 deletions src/eval/memory/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ impl<T: Sized + Clone> Array<T> {
// TODO: needs guard
/// Read only iterator
pub fn iter(&self) -> std::slice::Iter<T> {
debug_assert_ne!(self.length, usize::MAX);
debug_assert!(self.length < u32::MAX as usize);
self.as_slice().iter()
}

Expand Down Expand Up @@ -297,8 +299,8 @@ impl<T: Sized + Clone> Array<T> {

// Return pointer to allocated data for navigating to header (and
// marking during GC)
pub fn allocated_data(&self) -> Option<RefPtr<T>> {
self.data.ptr
pub fn allocated_data(&self) -> Option<RefPtr<u8>> {
self.data.ptr.map(|p| p.cast())
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/eval/memory/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub enum BlockError {
}

impl Block {
/// Defer to global allocatore to create new block of given size
pub fn new(size: usize) -> Result<Self, BlockError> {
if !size.is_power_of_two() {
Err(BlockError::BadSize)
Expand All @@ -48,11 +49,26 @@ impl Block {
if ptr.is_null() {
Err(BlockError::OOM)
} else {
if cfg!(debug_assertions) {
// fill memory with 0xff to aid debugging
let mem = std::slice::from_raw_parts_mut(ptr, size);
mem.fill(0xff);
}
Ok(NonNull::new_unchecked(ptr))
}
}
}

/// Fill areas that are meant to be dead with 0xff to aid debugging
#[cfg(debug_assertions)]
pub fn fill(&self, offset_bytes: usize, size_bytes: usize) {
unsafe {
let start = self.ptr.as_ptr().add(offset_bytes);
let mem = std::slice::from_raw_parts_mut(start, size_bytes);
mem.fill(0xff);
}
}

fn dealloc_block(ptr: NonNull<u8>, size: usize) {
unsafe { dealloc(ptr.as_ptr(), Layout::from_size_align_unchecked(size, size)) }
}
Expand Down
Loading

0 comments on commit ad89462

Please sign in to comment.