refactor primops

This commit is contained in:
2026-05-04 18:05:31 +08:00
parent 2e06c3ced5
commit 7fa2f44a87
14 changed files with 163 additions and 133 deletions
-1
View File
@@ -1,5 +1,4 @@
pub(crate) mod arithmetic;
pub(crate) mod builtins;
pub(crate) mod calls;
pub(crate) mod closures;
pub(crate) mod collections;
+6 -2
View File
@@ -51,13 +51,17 @@ impl<'gc> crate::Vm<'gc> {
return self.call(reader, mc, arg, resume_pc);
}
ThunkState::Blackhole => {
return self.finish_err(Error::eval_error("infinite recursion encountered"));
return self
.finish_err(Error::eval_error("infinite recursion encountered"));
}
}
}
};
if let Some(val) = namespace.as_gc::<AttrSet>().and_then(|attrs| attrs.lookup(name)) {
if let Some(val) = namespace
.as_gc::<AttrSet>()
.and_then(|attrs| attrs.lookup(name))
{
self.replace(0, val);
} else if counter + 1 == n as i32 {
return self.finish_err(Error::eval_error(format!(
+1
View File
@@ -30,6 +30,7 @@ mod instructions;
use bytecode_reader::BytecodeReader;
use forced::Forced;
use helpers::*;
mod primops;
type VmResult<T> = std::result::Result<T, VmError>;
+1
View File
@@ -0,0 +1 @@
@@ -1,128 +1,12 @@
use fix_builtins::PrimOpPhase;
use fix_error::Error;
use gc_arena::{Gc, Mutation, RefLock};
use num_enum::TryFromPrimitive as _;
use smallvec::SmallVec;
use crate::value::*;
use crate::{BytecodeReader, CallFrame, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
use crate::{BytecodeReader, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
impl<'gc> Vm<'gc> {
#[allow(clippy::too_many_lines)]
pub(crate) fn op_dispatch_primop(
&mut self,
ctx: &mut impl VmRuntimeCtx,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
use PrimOpPhase::*;
let phase_disc = reader.read_u8();
let Ok(phase) = PrimOpPhase::try_from_primitive(phase_disc) else {
return self.finish_err(Error::eval_error("invalid primop phase"));
};
match phase {
Abort => self.primop_abort(ctx, reader, mc),
DeepSeq => self.primop_deep_seq_force_top(reader, mc),
DeepSeqPush => self.primop_deep_seq_push(reader, mc),
DeepSeqLoop => self.primop_deep_seq_loop(reader, mc),
Seq => self.primop_seq(reader, mc),
FilterForceList => self.primop_filter_force_list(reader, mc),
FilterCallPred => self.primop_filter_call_pred(reader, mc),
FilterCheck => self.primop_filter_check(reader, mc),
ForceResultShallow => self.primop_force_result_shallow(ctx, reader, mc),
ForceResultShallowPush => self.primop_force_result_shallow_push(ctx, reader, mc),
ForceResultShallowLoop => self.primop_force_result_shallow_loop(reader, mc),
ForceResultDeepFinish => self.primop_force_result_deep_finish(ctx, reader, mc),
CallPattern => self.primop_call_pattern(ctx, reader, mc),
phase => todo!("primop phase {phase:?}"),
}
}
fn return_from_primop(&mut self, reader: &mut BytecodeReader<'_>) -> Step {
let Some(CallFrame {
pc: ret_pc,
thunk: _,
env,
}) = self.call_stack.pop()
else {
unreachable!()
};
reader.set_pc(ret_pc);
self.call_depth -= 1;
self.env = env;
Step::Continue(())
}
pub(crate) fn primop_filter_force_list(
&mut self,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
self.force_slot(0, reader, mc)?;
let list = match self.peek_forced(0).expect_gc::<List>() {
Ok(list) => list,
Err(got) => return self.finish_type_err(NixType::List, got),
};
if list.inner.borrow().is_empty() {
return self.return_from_primop(reader);
}
// prepare stack layout: [ pred list idx acc ]
self.push(Value::new_inline(0));
self.push(Value::new_gc(List::new_gc(mc)));
reader.set_pc(PrimOpPhase::FilterCallPred.ip() as usize);
Step::Continue(())
}
pub(crate) fn primop_filter_call_pred(
&mut self,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
self.force_slot(3, reader, mc)?;
let pred = self.peek_forced(3);
#[allow(clippy::unwrap_used)]
let idx = self.peek(1).as_inline::<i32>().unwrap();
#[allow(clippy::unwrap_used)]
let elem = self.peek_forced(2).as_gc::<List>().unwrap().inner.borrow()[idx as usize];
self.push(pred.relax());
self.call(reader, mc, elem, PrimOpPhase::FilterCheck.ip() as usize)
}
pub(crate) fn primop_filter_check(
&mut self,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
let ret = self.force_and_retry::<bool>(reader, mc)?;
#[allow(clippy::unwrap_used)]
let idx = self.peek(1).as_inline::<i32>().unwrap();
#[allow(clippy::unwrap_used)]
let list = self.peek_forced(2).as_gc::<List>().unwrap();
let list = list.inner.borrow();
#[allow(clippy::unwrap_used)]
let acc = self.peek_forced(0).as_gc::<List>().unwrap();
if ret {
let mut acc = acc.unlock(mc).borrow_mut();
acc.push(list[idx as usize]);
}
if idx as usize == list.len() - 1 {
let acc = self.pop();
let _ = self.pop(); // idx
let _ = self.pop(); // list
let _ = self.pop(); // pred
self.push(acc);
return self.return_from_primop(reader);
}
self.replace(1, Value::new_inline(idx + 1));
reader.set_pc(PrimOpPhase::FilterCallPred.ip() as usize);
Step::Continue(())
}
pub(crate) fn primop_seq(
&mut self,
reader: &mut BytecodeReader<'_>,
@@ -132,8 +16,7 @@ impl<'gc> Vm<'gc> {
self.force_slot(1, reader, mc)?;
let e2 = self.pop();
let _ = self.pop();
self.push(e2);
self.return_from_primop(reader)
self.return_from_primop(e2, reader)
}
pub(crate) fn primop_abort(
@@ -182,8 +65,7 @@ impl<'gc> Vm<'gc> {
if children.is_empty() {
let e2 = self.pop();
let _ = self.pop();
self.push(e2);
return self.return_from_primop(reader);
return self.return_from_primop(e2, reader);
}
let count = children.len() as i32;
@@ -212,7 +94,8 @@ impl<'gc> Vm<'gc> {
let _ = self.pop(); // counter
let _ = self.pop(); // worklist
let _ = self.pop(); // seen
return self.return_from_primop(reader);
let val = self.pop();
return self.return_from_primop(val, reader);
}
#[allow(clippy::unwrap_used)]
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+73
View File
@@ -0,0 +1,73 @@
use fix_builtins::PrimOpPhase;
use gc_arena::Mutation;
use crate::bytecode_reader::BytecodeReader;
use crate::value::*;
use crate::{Step, Vm};
impl<'gc> Vm<'gc> {
pub(crate) fn primop_filter_force_list(
&mut self,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
self.force_slot(0, reader, mc)?;
let list = match self.peek_forced(0).expect_gc::<List>() {
Ok(list) => list,
Err(got) => return self.finish_type_err(NixType::List, got),
};
if list.inner.borrow().is_empty() {
let val = self.pop();
return self.return_from_primop(val, reader);
}
// prepare stack layout: [ pred list idx acc ]
self.push(Value::new_inline(0));
self.push(Value::new_gc(List::new_gc(mc)));
reader.set_pc(PrimOpPhase::FilterCallPred.ip() as usize);
Step::Continue(())
}
pub(crate) fn primop_filter_call_pred(
&mut self,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
self.force_slot(3, reader, mc)?;
let pred = self.peek_forced(3);
#[allow(clippy::unwrap_used)]
let idx = self.peek(1).as_inline::<i32>().unwrap();
#[allow(clippy::unwrap_used)]
let elem = self.peek_forced(2).as_gc::<List>().unwrap().inner.borrow()[idx as usize];
self.push(pred.relax());
self.call(reader, mc, elem, PrimOpPhase::FilterCheck.ip() as usize)
}
pub(crate) fn primop_filter_check(
&mut self,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
let ret = self.force_and_retry::<bool>(reader, mc)?;
#[allow(clippy::unwrap_used)]
let idx = self.peek(1).as_inline::<i32>().unwrap();
#[allow(clippy::unwrap_used)]
let list = self.peek_forced(2).as_gc::<List>().unwrap();
let list = list.inner.borrow();
#[allow(clippy::unwrap_used)]
let acc = self.peek_forced(0).as_gc::<List>().unwrap();
if ret {
let mut acc = acc.unlock(mc).borrow_mut();
acc.push(list[idx as usize]);
}
if idx as usize == list.len() - 1 {
let acc = self.pop();
let _ = self.pop(); // idx
let _ = self.pop(); // list
let _ = self.pop(); // pred
return self.return_from_primop(acc, reader);
}
self.replace(1, Value::new_inline(idx + 1));
reader.set_pc(PrimOpPhase::FilterCallPred.ip() as usize);
Step::Continue(())
}
}
+70
View File
@@ -0,0 +1,70 @@
use fix_builtins::PrimOpPhase;
use fix_error::Error;
use gc_arena::Mutation;
use num_enum::TryFromPrimitive;
use crate::bytecode_reader::BytecodeReader;
use crate::value::Value;
use crate::{CallFrame, Step, Vm, VmRuntimeCtx};
mod attrs;
mod control;
mod conv;
mod io;
mod list;
mod path;
mod regex;
mod version;
impl<'gc> Vm<'gc> {
#[allow(clippy::too_many_lines)]
pub(crate) fn op_dispatch_primop(
&mut self,
ctx: &mut impl VmRuntimeCtx,
reader: &mut BytecodeReader<'_>,
mc: &Mutation<'gc>,
) -> Step {
use PrimOpPhase::*;
let phase_disc = reader.read_u8();
let Ok(phase) = PrimOpPhase::try_from_primitive(phase_disc) else {
return self.finish_err(Error::eval_error("invalid primop phase"));
};
match phase {
Abort => self.primop_abort(ctx, reader, mc),
DeepSeq => self.primop_deep_seq_force_top(reader, mc),
DeepSeqPush => self.primop_deep_seq_push(reader, mc),
DeepSeqLoop => self.primop_deep_seq_loop(reader, mc),
Seq => self.primop_seq(reader, mc),
FilterForceList => self.primop_filter_force_list(reader, mc),
FilterCallPred => self.primop_filter_call_pred(reader, mc),
FilterCheck => self.primop_filter_check(reader, mc),
ForceResultShallow => self.primop_force_result_shallow(ctx, reader, mc),
ForceResultShallowPush => self.primop_force_result_shallow_push(ctx, reader, mc),
ForceResultShallowLoop => self.primop_force_result_shallow_loop(reader, mc),
ForceResultDeepFinish => self.primop_force_result_deep_finish(ctx, reader, mc),
CallPattern => self.primop_call_pattern(ctx, reader, mc),
phase => todo!("primop phase {phase:?}"),
}
}
fn return_from_primop(&mut self, val: Value<'gc>, reader: &mut BytecodeReader<'_>) -> Step {
self.push(val);
let Some(CallFrame {
pc: ret_pc,
thunk: _,
env,
}) = self.call_stack.pop()
else {
unreachable!()
};
reader.set_pc(ret_pc);
self.call_depth -= 1;
self.env = env;
Step::Continue(())
}
}
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
View File
+1
View File
@@ -0,0 +1 @@
+2 -8
View File
@@ -524,14 +524,8 @@ pub(crate) type Thunk<'gc> = RefLock<ThunkState<'gc>>;
#[derive(Collect, Debug)]
#[collect(no_drop)]
pub(crate) enum ThunkState<'gc> {
Pending {
ip: usize,
env: GcEnv<'gc>,
},
Apply {
func: Value<'gc>,
arg: Value<'gc>,
},
Pending { ip: usize, env: GcEnv<'gc> },
Apply { func: Value<'gc>, arg: Value<'gc> },
Blackhole,
Evaluated(StrictValue<'gc>),
}