refactor primops
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
pub(crate) mod arithmetic;
|
pub(crate) mod arithmetic;
|
||||||
pub(crate) mod builtins;
|
|
||||||
pub(crate) mod calls;
|
pub(crate) mod calls;
|
||||||
pub(crate) mod closures;
|
pub(crate) mod closures;
|
||||||
pub(crate) mod collections;
|
pub(crate) mod collections;
|
||||||
|
|||||||
@@ -51,13 +51,17 @@ impl<'gc> crate::Vm<'gc> {
|
|||||||
return self.call(reader, mc, arg, resume_pc);
|
return self.call(reader, mc, arg, resume_pc);
|
||||||
}
|
}
|
||||||
ThunkState::Blackhole => {
|
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);
|
self.replace(0, val);
|
||||||
} else if counter + 1 == n as i32 {
|
} else if counter + 1 == n as i32 {
|
||||||
return self.finish_err(Error::eval_error(format!(
|
return self.finish_err(Error::eval_error(format!(
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ mod instructions;
|
|||||||
use bytecode_reader::BytecodeReader;
|
use bytecode_reader::BytecodeReader;
|
||||||
use forced::Forced;
|
use forced::Forced;
|
||||||
use helpers::*;
|
use helpers::*;
|
||||||
|
mod primops;
|
||||||
|
|
||||||
type VmResult<T> = std::result::Result<T, VmError>;
|
type VmResult<T> = std::result::Result<T, VmError>;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -1,128 +1,12 @@
|
|||||||
use fix_builtins::PrimOpPhase;
|
use fix_builtins::PrimOpPhase;
|
||||||
use fix_error::Error;
|
use fix_error::Error;
|
||||||
use gc_arena::{Gc, Mutation, RefLock};
|
use gc_arena::{Gc, Mutation, RefLock};
|
||||||
use num_enum::TryFromPrimitive as _;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::value::*;
|
use crate::value::*;
|
||||||
use crate::{BytecodeReader, CallFrame, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
|
use crate::{BytecodeReader, Step, Vm, VmRuntimeCtx, VmRuntimeCtxExt};
|
||||||
|
|
||||||
impl<'gc> Vm<'gc> {
|
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(
|
pub(crate) fn primop_seq(
|
||||||
&mut self,
|
&mut self,
|
||||||
reader: &mut BytecodeReader<'_>,
|
reader: &mut BytecodeReader<'_>,
|
||||||
@@ -132,8 +16,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
self.force_slot(1, reader, mc)?;
|
self.force_slot(1, reader, mc)?;
|
||||||
let e2 = self.pop();
|
let e2 = self.pop();
|
||||||
let _ = self.pop();
|
let _ = self.pop();
|
||||||
self.push(e2);
|
self.return_from_primop(e2, reader)
|
||||||
self.return_from_primop(reader)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn primop_abort(
|
pub(crate) fn primop_abort(
|
||||||
@@ -182,8 +65,7 @@ impl<'gc> Vm<'gc> {
|
|||||||
if children.is_empty() {
|
if children.is_empty() {
|
||||||
let e2 = self.pop();
|
let e2 = self.pop();
|
||||||
let _ = self.pop();
|
let _ = self.pop();
|
||||||
self.push(e2);
|
return self.return_from_primop(e2, reader);
|
||||||
return self.return_from_primop(reader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = children.len() as i32;
|
let count = children.len() as i32;
|
||||||
@@ -212,7 +94,8 @@ impl<'gc> Vm<'gc> {
|
|||||||
let _ = self.pop(); // counter
|
let _ = self.pop(); // counter
|
||||||
let _ = self.pop(); // worklist
|
let _ = self.pop(); // worklist
|
||||||
let _ = self.pop(); // seen
|
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)]
|
#[allow(clippy::unwrap_used)]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
+2
-8
@@ -524,14 +524,8 @@ pub(crate) type Thunk<'gc> = RefLock<ThunkState<'gc>>;
|
|||||||
#[derive(Collect, Debug)]
|
#[derive(Collect, Debug)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
pub(crate) enum ThunkState<'gc> {
|
pub(crate) enum ThunkState<'gc> {
|
||||||
Pending {
|
Pending { ip: usize, env: GcEnv<'gc> },
|
||||||
ip: usize,
|
Apply { func: Value<'gc>, arg: Value<'gc> },
|
||||||
env: GcEnv<'gc>,
|
|
||||||
},
|
|
||||||
Apply {
|
|
||||||
func: Value<'gc>,
|
|
||||||
arg: Value<'gc>,
|
|
||||||
},
|
|
||||||
Blackhole,
|
Blackhole,
|
||||||
Evaluated(StrictValue<'gc>),
|
Evaluated(StrictValue<'gc>),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user