feat: less gc (WIP)

This commit is contained in:
2025-06-02 14:18:59 +08:00
parent d3442e87e7
commit 51f8df9cca
11 changed files with 125 additions and 179 deletions

7
Cargo.lock generated
View File

@@ -324,6 +324,7 @@ dependencies = [
"inkwell",
"itertools",
"regex",
"replace_with",
"rnix",
"rustyline",
"thiserror 2.0.12",
@@ -398,6 +399,12 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "replace_with"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884"
[[package]]
name = "rnix"
version = "0.12.0"

View File

@@ -27,6 +27,7 @@ derive_more = { version = "2.0", features = ["full"] }
ecow = "0.2"
regex = "1.11"
hashbrown = "0.15"
replace_with = "0.1"
inkwell = { version = "0.6.0", features = ["llvm18-1"] }
gc-arena = { git = "https://github.com/kyren/gc-arena", rev = "d651e3b4363d525a2d502c2305bc73e291835c84", features= ["hashbrown"] }

View File

@@ -1,10 +1,12 @@
use std::rc::Rc;
use gc_arena::{Gc, Mutation};
use hashbrown::HashMap;
use crate::env::VmEnv;
use crate::ir::{DowngradeContext, Ir};
use crate::ty::common::Const;
use crate::ty::internal::{AttrSet, CoW, PrimOp, Value};
use crate::ty::internal::{AttrSet, PrimOp, Value};
use crate::vm::VM;
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<usize, Ir> {
@@ -21,7 +23,7 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> {
let Ok([mut first, second]): Result<[Value; 2], _> = args.try_into() else {
unreachable!()
};
first.add(second, mc);
first.add(second);
first.ok()
}),
PrimOp::new("sub", 2, |args, _, mc| {
@@ -29,7 +31,7 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> {
unreachable!()
};
second.neg();
first.add(second, mc);
first.add(second);
first.ok()
}),
PrimOp::new("mul", 2, |args, _, _| {
@@ -75,15 +77,15 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> {
let mut map = HashMap::new();
for primop in primops {
let primop = Gc::new(mc, primop);
let primop = Rc::new(primop);
env_map.insert(
vm.new_sym(format!("__{}", primop.name)),
Value::PrimOp(primop),
Value::PrimOp(primop.clone()),
);
map.insert(vm.new_sym(primop.name), Value::PrimOp(primop));
}
let sym = vm.new_sym("builtins");
let mut attrs = CoW::new(AttrSet::new(map), mc);
/* let mut attrs = CoW::new(AttrSet::new(map), mc);
unsafe {
attrs.make_cyclic(|attrs, this| {
let _ = attrs.push_attr(sym, Value::AttrSet(this));
@@ -91,7 +93,7 @@ pub fn vm_env<'gc>(vm: &VM, mc: &Mutation<'gc>) -> Gc<'gc, VmEnv<'gc>> {
}
let builtins = Value::AttrSet(attrs);
env_map.insert(sym, builtins);
env_map.insert(sym, builtins); */
// TODO:
// VmEnv::new(Gc::new(mc, env_map), mc)
VmEnv::new(Vec::new(), mc)

View File

@@ -11,7 +11,7 @@ pub struct Env<'gc, K: Hash + Eq + Collect<'gc>, V: Collect<'gc>> {
let_: Gc<'gc, LetEnv<'gc, V>>,
with: Gc<'gc, With<'gc, K, V>>,
args: Vec<V>,
gc_alloced: Vec<Gc<'gc, V>>,
jit_store: Vec<Box<V>>,
last: Option<Gc<'gc, Env<'gc, K, V>>>,
}
@@ -60,7 +60,7 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc,
},
),
args: Vec::new(),
gc_alloced: Vec::new(),
jit_store: Vec::new(),
last: None,
},
)
@@ -92,7 +92,7 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc,
let_: self.let_,
with: self.with,
last: Some(self),
gc_alloced: Vec::new(),
jit_store: Vec::new(),
args
},
)
@@ -110,7 +110,7 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc,
let_: self.let_.enter_let(map, mc),
with: self.with,
args: self.args.clone(),
gc_alloced: Vec::new(),
jit_store: Vec::new(),
last: Some(self),
},
)
@@ -128,17 +128,21 @@ impl<'gc, K: Hash + Eq + Clone + Collect<'gc>, V: Clone + Collect<'gc>> Env<'gc,
let_: self.let_,
with: self.with.enter(map, mc),
args: self.args.clone(),
gc_alloced: Vec::new(),
jit_store: Vec::new(),
last: Some(self),
},
)
}
#[inline]
pub fn alloc_gc(&mut self, val: V, mc: &Mutation<'gc>) -> &'gc V {
let gc = Gc::new(mc, val);
self.gc_alloced.push(gc);
gc.as_ref()
pub fn jit_store(&'gc mut self, val: V) -> &'gc V {
self.jit_store.push(Box::new(val));
&self.jit_store[self.jit_store.len() - 1]
}
#[inline]
pub fn jit_stored(&self) -> usize {
self.jit_store.len()
}
pub fn leave(&self) -> Gc<'gc, Self> {

View File

@@ -361,7 +361,7 @@ extern "C" fn helper_call<'gc>(
ValueTag::Function => {
let func = Value::from(func).unwrap_func();
let env = unsafe { &mut *env };
env.alloc_gc(func.call_compile(arg, vm, mc).unwrap(), mc).into()
func.call_compile(arg, vm, mc).unwrap().clone().into()
}
_ => todo!(),
}
@@ -369,19 +369,19 @@ extern "C" fn helper_call<'gc>(
extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_arg(idx).into();
let val: JITValue = env.lookup_arg(idx).clone().into();
val
}
extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_let(level, idx).into();
let val: JITValue = env.lookup_let(level, idx).clone().into();
val
}
extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_with(&sym).unwrap().into();
let val: JITValue = env.lookup_with(&sym).unwrap().clone().into();
val
}
@@ -399,7 +399,7 @@ extern "C" fn helper_force<'gc>(
let mc = unsafe { mc.as_ref() }.unwrap();
let thunk = Value::from(thunk).unwrap_thunk();
if let Some(val) = thunk.get_value() {
return val.into();
return val.clone().into();
}
let (opcodes, env) = thunk.suspend(mc).unwrap();
let func = unsafe { jit.as_ref() }
@@ -414,9 +414,9 @@ extern "C" fn helper_force<'gc>(
extern "C" fn helper_new_thunk<'gc>(opcodes: *const OpCodes, env: *mut VmEnv<'gc>, mc: *const Mutation<'gc>) -> JITValue {
let mc = unsafe { &*mc };
let env = unsafe { &mut *env };
env.alloc_gc(Value::Thunk(Thunk::new(
Value::Thunk(Thunk::new(
unsafe { opcodes.as_ref() }.unwrap(),
mc
)), mc)
))
.into()
}

View File

@@ -1,4 +1,5 @@
use std::marker::PhantomData;
use std::rc::Rc;
use std::ops::Deref;
use gc_arena::{Collect, Gc, Mutation};
@@ -63,7 +64,7 @@ impl<'gc> From<JITValue> for Value<'gc> {
match value.tag {
Int => Value::Int(unsafe { value.data.int }),
Null => Value::Null,
Function => Value::Func(unsafe { Gc::from_ptr(value.data.ptr as *const _) }),
Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }),
Thunk => Value::Thunk(self::Thunk {
thunk: unsafe { Gc::from_ptr(value.data.ptr as *const _) },
}),
@@ -72,19 +73,21 @@ impl<'gc> From<JITValue> for Value<'gc> {
}
}
impl From<&Value<'_>> for JITValue {
fn from(value: &Value) -> Self {
impl From<Value<'_>> for JITValue {
fn from(value: Value) -> Self {
match value {
&Value::Int(int) => JITValue {
Value::Int(int) => JITValue {
tag: ValueTag::Int,
data: JITValueData { int },
},
&Value::Func(func) => JITValue {
Value::Func(func) => {
JITValue {
tag: ValueTag::Function,
data: JITValueData {
ptr: Gc::as_ptr(func) as *const _,
},
ptr: Rc::into_raw(func) as *const _,
},
}
}
Value::Thunk(thunk) => JITValue {
tag: ValueTag::Thunk,
data: JITValueData {

View File

@@ -12,12 +12,17 @@ use super::super::public as p;
use super::Value;
#[repr(transparent)]
#[derive(Constructor, Clone, PartialEq, Collect)]
#[collect(no_drop)]
#[derive(Constructor, Clone, PartialEq)]
pub struct AttrSet<'gc> {
data: HashMap<usize, Value<'gc>>,
}
unsafe impl<'gc> Collect<'gc> for AttrSet<'gc> {
fn trace<T: gc_arena::collect::Trace<'gc>>(&self, cc: &mut T) {
self.data.trace(cc);
}
}
impl<'gc> From<HashMap<usize, Value<'gc>>> for AttrSet<'gc> {
fn from(data: HashMap<usize, Value<'gc>>) -> Self {
Self { data }

View File

@@ -7,12 +7,17 @@ use crate::env::VmEnv;
use super::Value;
#[derive(Clone, PartialEq, Collect)]
#[collect(no_drop)]
#[derive(Clone, PartialEq)]
pub struct List<'gc> {
data: Vec<Value<'gc>>,
}
unsafe impl<'gc> Collect<'gc> for List<'gc> {
fn trace<T: gc_arena::collect::Trace<'gc>>(&self, cc: &mut T) {
self.data.trace(cc);
}
}
impl<'gc> List<'gc> {
pub fn new() -> Self {
List {

View File

@@ -1,12 +1,12 @@
use std::cell::Cell;
use std::hash::Hash;
use std::ops::Deref;
use std::rc::Rc;
use derive_more::{IsVariant, Unwrap};
use gc_arena::Mutation;
use gc_arena::lock::RefLock;
use gc_arena::{Collect, Gc, lock::GcRefLock};
use hashbrown::HashSet;
use replace_with::replace_with_or_abort;
use super::common::*;
use super::public as p;
@@ -27,95 +27,22 @@ pub use func::*;
pub use list::List;
pub use primop::*;
#[derive(Collect)]
#[collect(unsafe_drop)]
pub struct CoW<'gc, T: Clone + Collect<'gc>> {
inner: Gc<'gc, CoWInner<'gc, T>>,
}
#[derive(Collect)]
#[collect(no_drop)]
struct CoWInner<'gc, T: Clone + Collect<'gc>> {
ref_count: Cell<usize>,
data: GcRefLock<'gc, T>,
}
#[allow(mutable_transmutes)]
impl<'gc, T: Clone + Collect<'gc>> CoW<'gc, T> {
pub fn new(data: T, mc: &Mutation<'gc>) -> Self {
Self {
inner: Gc::new(mc, CoWInner::new(data, mc)),
}
}
pub unsafe fn make_cyclic(&mut self, f: impl FnOnce(&mut T, Self), mc: &Mutation<'gc>) {
let inner = self.inner;
self.make_mut(|self_mut| f(self_mut, Self { inner }), mc);
self.inner.ref_count.set(self.inner.ref_count.get() + 1);
}
pub fn make_mut<U>(&mut self, f: impl FnOnce(&mut T) -> U, mc: &Mutation<'gc>) -> U {
if self.inner.ref_count.get() == 1 {
f(&mut *self.inner.data.borrow_mut(mc))
} else {
*self = CoW::new(self.inner.data.borrow().clone(), mc);
f(&mut *self.inner.data.borrow_mut(mc))
}
}
pub fn as_ptr(&self) -> *const T {
&*self.as_ref() as *const T
}
pub fn as_ref(&self) -> std::cell::Ref<'_, T> {
self.inner.data.borrow()
}
}
impl<'gc, T: Clone + Collect<'gc>> Deref for CoW<'gc, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.as_ptr().as_ref() }.unwrap()
}
}
impl<'gc, T: Clone + Collect<'gc>> Clone for CoW<'gc, T> {
fn clone(&self) -> Self {
self.inner.ref_count.set(self.inner.ref_count.get() + 1);
Self { inner: self.inner }
}
}
impl<'gc, T: Clone + Collect<'gc>> Drop for CoW<'gc, T> {
fn drop(&mut self) {
self.inner.ref_count.set(self.inner.ref_count.get() - 1);
}
}
impl<'gc, T: Clone + Collect<'gc>> CoWInner<'gc, T> {
fn new(data: T, mc: &Mutation<'gc>) -> Self {
Self {
ref_count: Cell::new(1),
data: Gc::new(mc, RefLock::new(data))
}
}
}
#[derive(IsVariant, Unwrap, Clone, Collect)]
#[collect(no_drop)]
// #[derive(IsVariant, Unwrap, Clone)]
pub enum Value<'gc> {
Int(i64),
Float(f64),
Bool(bool),
String(CoW<'gc, String>),
String(Rc<String>),
Null,
Thunk(Thunk<'gc>),
AttrSet(CoW<'gc, AttrSet<'gc>>),
List(CoW<'gc, List<'gc>>),
Catchable(Gc<'gc, String>),
PrimOp(Gc<'gc, PrimOp>),
PartialPrimOp(CoW<'gc, PartialPrimOp<'gc>>),
Func(Gc<'gc, Func<'gc>>),
AttrSet(Rc<AttrSet<'gc>>),
List(Rc<List<'gc>>),
Catchable(Rc<String>),
PrimOp(Rc<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp<'gc>>),
Func(Rc<Func<'gc>>),
}
impl Hash for Value<'_> {
@@ -123,18 +50,9 @@ impl Hash for Value<'_> {
use Value::*;
std::mem::discriminant(self).hash(state);
match self {
Int(x) => x.hash(state),
Float(x) => x.to_bits().hash(state),
Bool(x) => x.hash(state),
Null => (),
String(x) => x.as_ptr().hash(state),
Thunk(x) => (x as *const self::Thunk).hash(state),
AttrSet(x) => x.as_ptr().hash(state),
List(x) => x.as_ptr().hash(state),
Catchable(x) => x.hash(state),
PrimOp(x) => (x.as_ref() as *const self::PrimOp).hash(state),
PartialPrimOp(x) => x.as_ptr().hash(state),
Func(x) => (x.as_ref() as *const self::Func).hash(state),
AttrSet(x) => Rc::as_ptr(x).hash(state),
List(x) => Rc::as_ptr(x).hash(state),
_ => 0.hash(state),
}
}
}
@@ -161,8 +79,8 @@ impl<'gc> PartialEq for Value<'gc> {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(AttrSet(a), AttrSet(b)) => a.as_ptr().eq(&b.as_ptr()),
(List(a), List(b)) => a.as_ptr().eq(&b.as_ptr()),
(AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
(List(a), List(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
_ => false,
}
}
@@ -318,22 +236,23 @@ impl<'gc> Value<'gc> {
}
}
pub fn add(&mut self, other: Self, mc: &Mutation<'gc>) {
pub fn add(&mut self, other: Self) {
use Value::*;
*self = match (&*self, other) {
replace_with_or_abort(self, |a| {
match (a, other) {
(Int(a), Int(b)) => Int(a + b),
(Int(a), Float(b)) => Float(*a as f64 + b),
(Int(a), Float(b)) => Float(a as f64 + b),
(Float(a), Int(b)) => Float(a + b as f64),
(Float(a), Float(b)) => Float(a + b),
(String(a), String(b)) => {
let mut a = a.clone();
a.make_mut(|a| a.push_str(b.as_str()), mc);
(String(mut a), String(b)) => {
Rc::make_mut(&mut a).push_str(&b);
String(a)
}
(Value::Catchable(_), _) => return,
(a @ Value::Catchable(_), _) => a,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
});
}
pub fn mul(&mut self, other: Self) {
@@ -367,11 +286,10 @@ impl<'gc> Value<'gc> {
Ok(())
}
pub fn concat_string(&mut self, mut other: Self, mc: &Mutation<'gc>) -> &mut Self {
pub fn concat_string(&mut self, mut other: Self) -> &mut Self {
match (self.coerce_to_string(), other.coerce_to_string()) {
(Value::String(a), Value::String(b)) => {
let mut a = a.clone();
a.make_mut(|a| a.push_str(b.as_str()), mc);
Rc::make_mut(a).push_str(b.as_str());
}
(_, Value::Catchable(_)) => *self = other,
(Value::Catchable(_), _) => (),
@@ -380,9 +298,9 @@ impl<'gc> Value<'gc> {
self
}
pub fn push(&mut self, elem: Self, mc: &Mutation<'gc>) -> &mut Self {
pub fn push(&mut self, elem: Self) -> &mut Self {
if let Value::List(list) = self {
list.make_mut(|list| list.push(elem), mc);
Rc::make_mut(list).push(elem);
} else if let Value::Catchable(_) = self {
} else if let Value::Catchable(_) = elem {
*self = elem;
@@ -392,23 +310,23 @@ impl<'gc> Value<'gc> {
self
}
pub fn concat(&mut self, other: Self, mc: &Mutation<'gc>) {
pub fn concat(&mut self, other: Self) {
if let x @ Value::Catchable(_) = other {
*self = x;
return;
}
match (self, other) {
(Value::List(a), Value::List(b)) => {
a.make_mut(|a| a.concat(&b), mc);
Rc::make_mut(a).concat(&b);
}
(Value::Catchable(_), _) => (),
_ => todo!(),
}
}
pub fn push_attr(&mut self, sym: usize, val: Self, mc: &Mutation<'gc>) -> &mut Self {
pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self {
if let Value::AttrSet(attrs) = self {
attrs.make_mut(|attrs| attrs.push_attr(sym, val), mc)
Rc::make_mut(attrs).push_attr(sym, val);
} else if let Value::Catchable(_) = self {
} else if let Value::Catchable(_) = val {
*self = val
@@ -418,14 +336,14 @@ impl<'gc> Value<'gc> {
self
}
pub fn update(&mut self, other: Self, mc: &Mutation<'gc>) {
pub fn update(&mut self, other: Self) {
if let x @ Value::Catchable(_) = other {
*self = x;
return;
}
match (self, other) {
(Value::AttrSet(a), Value::AttrSet(b)) => {
a.make_mut(|a| a.update(&b), mc)
Rc::make_mut(a).update(&b);
}
(Value::Catchable(_), _) => (),
_ => todo!(),

View File

@@ -1,3 +1,5 @@
use std::rc::Rc;
use derive_more::Constructor;
use gc_arena::Collect;
use gc_arena::Mutation;
@@ -5,7 +7,6 @@ use gc_arena::Mutation;
use crate::error::Result;
use crate::vm::VM;
use super::CoW;
use super::Value;
#[derive(Debug, Clone, Constructor, Collect)]
@@ -27,14 +28,13 @@ impl PrimOp {
let mut args = Vec::with_capacity(self.arity);
args.push(arg);
if self.arity > 1 {
Value::PartialPrimOp(CoW::new(
Value::PartialPrimOp(Rc::new(
PartialPrimOp {
name: self.name,
arity: self.arity - 1,
args,
func: self.func,
},
mc,
))
.ok()
} else {
@@ -67,13 +67,14 @@ impl PartialEq for PartialPrimOp<'_> {
impl<'gc> PartialPrimOp<'gc> {
pub fn call(
self: &mut CoW<'gc, Self>,
self: &mut Rc<Self>,
arg: Value<'gc>,
vm: &VM,
mc: &Mutation<'gc>,
) -> Result<Value<'gc>> {
let func = self.func;
let Some(ret) = self.make_mut(|self_mut| {
let Some(ret) = ({
let self_mut = Rc::make_mut(self);
self_mut.args.push(arg);
self_mut.arity -= 1;
if self_mut.arity == 0 {
@@ -81,7 +82,7 @@ impl<'gc> PartialPrimOp<'gc> {
} else {
None
}
}, mc) else {
}) else {
return Value::PartialPrimOp(self.clone()).ok();
};
ret

View File

@@ -1,4 +1,5 @@
use std::cell::RefCell;
use std::rc::Rc;
use gc_arena::arena::CollectionPhase;
use gc_arena::{Arena, Collect, Gc, Mutation, Rootable};
@@ -123,7 +124,6 @@ pub fn eval<T, F: for<'gc> FnOnce(Value<'gc>, &mut GcRoot<'gc>, &Mutation<'gc>)
}
}
}
arena.collect_debt();
arena.mutate_root(|mc, root| {
assert_eq!(root.stack.len(), 1);
let ret = root.stack.pop();
@@ -158,7 +158,7 @@ fn single_op<'gc, const CAP: usize>(
Const::Int(x) => Value::Int(x),
Const::Float(x) => Value::Float(x),
Const::Bool(x) => Value::Bool(x),
Const::String(x) => Value::String(CoW::new(x.into(), mc)),
Const::String(x) => Value::String(Rc::new(x.into())),
Const::Null => Value::Null,
})?,
OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx), mc)))?,
@@ -199,7 +199,7 @@ fn single_op<'gc, const CAP: usize>(
}
OpCode::Func { idx } => {
let func = vm.get_func(idx);
stack.push(Value::Func(Gc::new(mc, Func::new(func, *env, mc))))?;
stack.push(Value::Func(Rc::new(Func::new(func, *env, mc))))?;
}
OpCode::Arg { level } => {
stack.push(env.lookup_arg(level).clone())?;
@@ -217,10 +217,10 @@ fn single_op<'gc, const CAP: usize>(
let mut rhs = stack.pop();
let lhs = stack.tos_mut();
match op {
Add => lhs.add(rhs, mc),
Add => lhs.add(rhs),
Sub => {
rhs.neg();
lhs.add(rhs, mc);
lhs.add(rhs);
}
Mul => lhs.mul(rhs),
Div => lhs.div(rhs)?,
@@ -228,26 +228,26 @@ fn single_op<'gc, const CAP: usize>(
Or => lhs.or(rhs),
Eq => Value::eq(lhs, rhs),
Lt => lhs.lt(rhs),
Con => lhs.concat(rhs, mc),
Upd => lhs.update(rhs, mc),
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
}
}
OpCode::ConcatString => {
let rhs = stack.pop();
stack.tos_mut().concat_string(rhs, mc);
stack.tos_mut().concat_string(rhs);
}
OpCode::Path => {
todo!()
}
OpCode::List { cap } => {
stack.push(Value::List(CoW::new(List::with_capacity(cap), mc)))?;
stack.push(Value::List(Rc::new(List::with_capacity(cap))))?;
}
OpCode::PushElem => {
let elem = stack.pop();
stack.tos_mut().push(elem, mc);
stack.tos_mut().push(elem);
}
OpCode::AttrSet { cap } => {
stack.push(Value::AttrSet(CoW::new(AttrSet::with_capacity(cap), mc)))?;
stack.push(Value::AttrSet(Rc::new(AttrSet::with_capacity(cap))))?;
}
OpCode::FinalizeLet => {
let mut list = stack.pop().unwrap_list();
@@ -256,17 +256,17 @@ fn single_op<'gc, const CAP: usize>(
.clone()
.into_inner();
*env = env.enter_let(map, mc);
list.make_mut(|list| list.capture(*env, mc), mc);
Rc::make_mut(&mut list).capture(*env, mc);
}
OpCode::PushStaticAttr { name } => {
let val = stack.pop();
stack.tos_mut().push_attr(name, val, mc);
stack.tos_mut().push_attr(name, val);
}
OpCode::PushDynamicAttr => {
let val = stack.pop();
let sym = stack.pop();
let sym = vm.new_sym::<&str>(&sym.unwrap_string());
stack.tos_mut().push_attr(sym, val, mc);
stack.tos_mut().push_attr(sym, val);
}
OpCode::Select { sym } => {
stack.tos_mut().select(sym, vm)?;