refactor(tvix/eval): introduce UpvalueCarrier trait

This trait abstracts over the commonalities of upvalue handling
between closures and thunks.

It allows the VM to simplify the code used for setting up upvalues,
without duplicating between the two different types.

Note that this does not yet refactor the VM code to optimally make use
of this.

Change-Id: If8de5181f26ae1fa00d554f1ae6ea473ee4b6070
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6347
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
Vincent Ambo 2022-08-29 18:07:58 +03:00 committed by tazjin
parent 8033a7abae
commit 25c62dd0ef
5 changed files with 81 additions and 18 deletions

View file

@ -4,6 +4,7 @@ mod compiler;
mod errors; mod errors;
mod eval; mod eval;
mod opcode; mod opcode;
mod upvalues;
mod value; mod value;
mod vm; mod vm;
mod warnings; mod warnings;

39
tvix/eval/src/upvalues.rs Normal file
View file

@ -0,0 +1,39 @@
//! This module encapsulates some logic for upvalue handling, which is
//! relevant to both thunks (delayed computations for lazy-evaluation)
//! as well as closures (lambdas that capture variables from the
//! surrounding scope).
use std::cell::{Ref, RefMut};
use crate::{opcode::UpvalueIdx, Value};
/// `UpvalueCarrier` is implemented by all types that carry upvalues.
pub trait UpvalueCarrier {
fn upvalue_count(&self) -> usize;
/// Read-only accessor for the stored upvalues.
fn upvalues(&self) -> Ref<'_, [Value]>;
/// Mutable accessor for stored upvalues.
fn upvalues_mut(&self) -> RefMut<'_, Vec<Value>>;
/// Read an upvalue at the given index.
fn upvalue(&self, idx: UpvalueIdx) -> Ref<'_, Value> {
Ref::map(self.upvalues(), |v| &v[idx.0])
}
/// Push an upvalue at the end of the upvalue list.
fn push_upvalue(&self, value: Value) {
self.upvalues_mut().push(value);
}
/// Resolve deferred upvalues from the provided stack slice,
/// mutating them in the internal upvalue slots.
fn resolve_deferred_upvalues(&self, stack: &[Value]) {
for upvalue in self.upvalues_mut().iter_mut() {
if let Value::DeferredUpvalue(idx) = upvalue {
*upvalue = stack[idx.0].clone();
}
}
}
}

View file

@ -1,10 +1,10 @@
//! This module implements the runtime representation of functions. //! This module implements the runtime representation of functions.
use std::{ use std::{
cell::{Ref, RefCell}, cell::{Ref, RefCell, RefMut},
rc::Rc, rc::Rc,
}; };
use crate::{chunk::Chunk, opcode::UpvalueIdx, Value}; use crate::{chunk::Chunk, upvalues::UpvalueCarrier, Value};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Lambda { pub struct Lambda {
@ -49,26 +49,21 @@ impl Closure {
Ref::map(self.0.borrow(), |c| &c.lambda.chunk) Ref::map(self.0.borrow(), |c| &c.lambda.chunk)
} }
pub fn upvalue(&self, idx: UpvalueIdx) -> Ref<'_, Value> { pub fn lambda(&self) -> Rc<Lambda> {
Ref::map(self.0.borrow(), |c| &c.upvalues[idx.0]) self.0.borrow().lambda.clone()
}
} }
pub fn upvalue_count(&self) -> usize { impl UpvalueCarrier for Closure {
fn upvalue_count(&self) -> usize {
self.0.borrow().lambda.upvalue_count self.0.borrow().lambda.upvalue_count
} }
pub fn push_upvalue(&self, value: Value) { fn upvalues(&self) -> Ref<'_, [Value]> {
self.0.borrow_mut().upvalues.push(value) Ref::map(self.0.borrow(), |c| c.upvalues.as_slice())
} }
/// Resolve the deferred upvalues in the closure from a slice of fn upvalues_mut(&self) -> RefMut<'_, Vec<Value>> {
/// the current stack, using the indices stored in the deferred RefMut::map(self.0.borrow_mut(), |c| &mut c.upvalues)
/// values.
pub fn resolve_deferred_upvalues(&self, stack: &[Value]) {
for upvalue in self.0.borrow_mut().upvalues.iter_mut() {
if let Value::DeferredUpvalue(idx) = upvalue {
*upvalue = stack[idx.0].clone();
}
}
} }
} }

View file

@ -18,9 +18,12 @@
//! object, but when forcing a thunk, the runtime *must* mutate the //! object, but when forcing a thunk, the runtime *must* mutate the
//! memoisable slot. //! memoisable slot.
use std::{cell::RefCell, rc::Rc}; use std::{
cell::{Ref, RefCell, RefMut},
rc::Rc,
};
use crate::Value; use crate::{upvalues::UpvalueCarrier, Value};
use super::Lambda; use super::Lambda;
@ -53,3 +56,27 @@ impl Thunk {
}))) })))
} }
} }
impl UpvalueCarrier for Thunk {
fn upvalue_count(&self) -> usize {
if let ThunkRepr::Suspended { lambda, .. } = &*self.0.borrow() {
return lambda.upvalue_count;
}
panic!("upvalues() on non-suspended thunk");
}
fn upvalues(&self) -> Ref<'_, [Value]> {
Ref::map(self.0.borrow(), |thunk| match thunk {
ThunkRepr::Suspended { upvalues, .. } => upvalues.as_slice(),
_ => panic!("upvalues() on non-suspended thunk"),
})
}
fn upvalues_mut(&self) -> RefMut<'_, Vec<Value>> {
RefMut::map(self.0.borrow_mut(), |thunk| match thunk {
ThunkRepr::Suspended { upvalues, .. } => upvalues,
_ => panic!("upvalues() on non-suspended thunk"),
})
}
}

View file

@ -7,6 +7,7 @@ use crate::{
chunk::Chunk, chunk::Chunk,
errors::{Error, ErrorKind, EvalResult}, errors::{Error, ErrorKind, EvalResult},
opcode::{ConstantIdx, Count, JumpOffset, OpCode, StackIdx}, opcode::{ConstantIdx, Count, JumpOffset, OpCode, StackIdx},
upvalues::UpvalueCarrier,
value::{Closure, Lambda, NixAttrs, NixList, Value}, value::{Closure, Lambda, NixAttrs, NixList, Value},
}; };