From b5246c04aee686fa3c65647e794d59646fb31900 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Mon, 4 May 2026 18:05:31 +0800 Subject: [PATCH] refactor primops --- fix-vm/src/instructions/mod.rs | 1 - fix-vm/src/instructions/with_scope.rs | 8 +- fix-vm/src/lib.rs | 1 + fix-vm/src/primops/attrs.rs | 1 + .../builtins.rs => primops/control.rs} | 127 +----------------- fix-vm/src/primops/conv.rs | 1 + fix-vm/src/primops/io.rs | 1 + fix-vm/src/primops/list.rs | 73 ++++++++++ fix-vm/src/primops/mod.rs | 70 ++++++++++ fix-vm/src/primops/path.rs | 1 + fix-vm/src/primops/regex.rs | 1 + fix-vm/src/primops/string.rs | 0 fix-vm/src/primops/version.rs | 1 + fix-vm/src/value.rs | 10 +- 14 files changed, 163 insertions(+), 133 deletions(-) create mode 100644 fix-vm/src/primops/attrs.rs rename fix-vm/src/{instructions/builtins.rs => primops/control.rs} (69%) create mode 100644 fix-vm/src/primops/conv.rs create mode 100644 fix-vm/src/primops/io.rs create mode 100644 fix-vm/src/primops/list.rs create mode 100644 fix-vm/src/primops/mod.rs create mode 100644 fix-vm/src/primops/path.rs create mode 100644 fix-vm/src/primops/regex.rs create mode 100644 fix-vm/src/primops/string.rs create mode 100644 fix-vm/src/primops/version.rs diff --git a/fix-vm/src/instructions/mod.rs b/fix-vm/src/instructions/mod.rs index 9a87d84..d5f222c 100644 --- a/fix-vm/src/instructions/mod.rs +++ b/fix-vm/src/instructions/mod.rs @@ -1,5 +1,4 @@ pub(crate) mod arithmetic; -pub(crate) mod builtins; pub(crate) mod calls; pub(crate) mod closures; pub(crate) mod collections; diff --git a/fix-vm/src/instructions/with_scope.rs b/fix-vm/src/instructions/with_scope.rs index aa9151e..c8e0555 100644 --- a/fix-vm/src/instructions/with_scope.rs +++ b/fix-vm/src/instructions/with_scope.rs @@ -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::().and_then(|attrs| attrs.lookup(name)) { + if let Some(val) = namespace + .as_gc::() + .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!( diff --git a/fix-vm/src/lib.rs b/fix-vm/src/lib.rs index 5f150d8..62a870d 100644 --- a/fix-vm/src/lib.rs +++ b/fix-vm/src/lib.rs @@ -30,6 +30,7 @@ mod instructions; use bytecode_reader::BytecodeReader; use forced::Forced; use helpers::*; +mod primops; type VmResult = std::result::Result; diff --git a/fix-vm/src/primops/attrs.rs b/fix-vm/src/primops/attrs.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fix-vm/src/primops/attrs.rs @@ -0,0 +1 @@ + diff --git a/fix-vm/src/instructions/builtins.rs b/fix-vm/src/primops/control.rs similarity index 69% rename from fix-vm/src/instructions/builtins.rs rename to fix-vm/src/primops/control.rs index 0cb6486..ae19951 100644 --- a/fix-vm/src/instructions/builtins.rs +++ b/fix-vm/src/primops/control.rs @@ -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::() { - 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::().unwrap(); - #[allow(clippy::unwrap_used)] - let elem = self.peek_forced(2).as_gc::().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::(reader, mc)?; - #[allow(clippy::unwrap_used)] - let idx = self.peek(1).as_inline::().unwrap(); - #[allow(clippy::unwrap_used)] - let list = self.peek_forced(2).as_gc::().unwrap(); - let list = list.inner.borrow(); - #[allow(clippy::unwrap_used)] - let acc = self.peek_forced(0).as_gc::().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)] diff --git a/fix-vm/src/primops/conv.rs b/fix-vm/src/primops/conv.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fix-vm/src/primops/conv.rs @@ -0,0 +1 @@ + diff --git a/fix-vm/src/primops/io.rs b/fix-vm/src/primops/io.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fix-vm/src/primops/io.rs @@ -0,0 +1 @@ + diff --git a/fix-vm/src/primops/list.rs b/fix-vm/src/primops/list.rs new file mode 100644 index 0000000..ce1eb45 --- /dev/null +++ b/fix-vm/src/primops/list.rs @@ -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::() { + 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::().unwrap(); + #[allow(clippy::unwrap_used)] + let elem = self.peek_forced(2).as_gc::().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::(reader, mc)?; + #[allow(clippy::unwrap_used)] + let idx = self.peek(1).as_inline::().unwrap(); + #[allow(clippy::unwrap_used)] + let list = self.peek_forced(2).as_gc::().unwrap(); + let list = list.inner.borrow(); + #[allow(clippy::unwrap_used)] + let acc = self.peek_forced(0).as_gc::().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(()) + } +} diff --git a/fix-vm/src/primops/mod.rs b/fix-vm/src/primops/mod.rs new file mode 100644 index 0000000..e1109ba --- /dev/null +++ b/fix-vm/src/primops/mod.rs @@ -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(()) + } +} diff --git a/fix-vm/src/primops/path.rs b/fix-vm/src/primops/path.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fix-vm/src/primops/path.rs @@ -0,0 +1 @@ + diff --git a/fix-vm/src/primops/regex.rs b/fix-vm/src/primops/regex.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fix-vm/src/primops/regex.rs @@ -0,0 +1 @@ + diff --git a/fix-vm/src/primops/string.rs b/fix-vm/src/primops/string.rs new file mode 100644 index 0000000..e69de29 diff --git a/fix-vm/src/primops/version.rs b/fix-vm/src/primops/version.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fix-vm/src/primops/version.rs @@ -0,0 +1 @@ + diff --git a/fix-vm/src/value.rs b/fix-vm/src/value.rs index f769161..e91cedb 100644 --- a/fix-vm/src/value.rs +++ b/fix-vm/src/value.rs @@ -524,14 +524,8 @@ pub(crate) type Thunk<'gc> = RefLock>; #[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>), }