switch to stack model for plutus eval

Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
Kasey White 2022-07-28 02:40:44 -04:00 committed by Lucas
parent 4acbd704e0
commit a3a175d74b
2 changed files with 95 additions and 35 deletions

View File

@ -12,12 +12,30 @@ pub use error::Error;
use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime}; use self::{cost_model::CostModel, error::Type, runtime::BuiltinRuntime};
enum MachineStep {
Return(Context, Value),
Compute(Context, Vec<Value>, Term<NamedDeBruijn>),
Done(Term<NamedDeBruijn>),
}
impl TryFrom<Option<MachineStep>> for Term<NamedDeBruijn> {
type Error = Error;
fn try_from(value: Option<MachineStep>) -> Result<Self, Error> {
match value {
Some(MachineStep::Done(term)) => Ok(term),
_ => Err(Error::MachineNeverReachedDone),
}
}
}
pub struct Machine { pub struct Machine {
costs: CostModel, costs: CostModel,
pub ex_budget: ExBudget, pub ex_budget: ExBudget,
slippage: u32, slippage: u32,
unbudgeted_steps: [u32; 8], unbudgeted_steps: [u32; 8],
pub logs: Vec<String>, pub logs: Vec<String>,
stack: Vec<MachineStep>,
} }
impl Machine { impl Machine {
@ -28,15 +46,37 @@ impl Machine {
slippage, slippage,
unbudgeted_steps: [0; 8], unbudgeted_steps: [0; 8],
logs: vec![], logs: vec![],
stack: vec![],
} }
} }
pub fn run(&mut self, term: &Term<NamedDeBruijn>) -> Result<Term<NamedDeBruijn>, Error> { pub fn run(&mut self, term: &Term<NamedDeBruijn>) -> Result<Term<NamedDeBruijn>, Error> {
use MachineStep::*;
let startup_budget = self.costs.machine_costs.get(StepKind::StartUp); let startup_budget = self.costs.machine_costs.get(StepKind::StartUp);
self.spend_budget(startup_budget)?; self.spend_budget(startup_budget)?;
self.compute(Context::NoFrame, vec![], term) self.stack
.push(Compute(Context::NoFrame, vec![], term.clone()));
while let Some(step) = self.stack.pop() {
match step {
Compute(context, env, t) => {
self.compute(context, env, &t)?;
}
Return(context, value) => {
self.return_compute(context, value)?;
}
d @ Done(_) => {
self.stack.push(d);
break;
}
};
}
self.stack.pop().try_into()
} }
fn compute( fn compute(
@ -44,19 +84,22 @@ impl Machine {
context: Context, context: Context,
env: Vec<Value>, env: Vec<Value>,
term: &Term<NamedDeBruijn>, term: &Term<NamedDeBruijn>,
) -> Result<Term<NamedDeBruijn>, Error> { ) -> Result<(), Error> {
match term { match term {
Term::Var(name) => { Term::Var(name) => {
self.step_and_maybe_spend(StepKind::Var)?; self.step_and_maybe_spend(StepKind::Var)?;
let val = self.lookup_var(name.clone(), env)?; let val = self.lookup_var(name.clone(), env)?;
self.return_compute(context, val) self.stack.push(MachineStep::Return(context, val));
} }
Term::Delay(body) => { Term::Delay(body) => {
self.step_and_maybe_spend(StepKind::Delay)?; self.step_and_maybe_spend(StepKind::Delay)?;
self.return_compute(context, Value::Delay(*body.clone(), env)) self.stack.push(MachineStep::Return(
context,
Value::Delay(*body.clone(), env),
));
} }
Term::Lambda { Term::Lambda {
parameter_name, parameter_name,
@ -64,63 +107,70 @@ impl Machine {
} => { } => {
self.step_and_maybe_spend(StepKind::Lambda)?; self.step_and_maybe_spend(StepKind::Lambda)?;
self.return_compute( self.stack.push(MachineStep::Return(
context, context,
Value::Lambda { Value::Lambda {
parameter_name: parameter_name.clone(), parameter_name: parameter_name.clone(),
body: *body.clone(), body: *body.clone(),
env, env,
}, },
) ));
} }
Term::Apply { function, argument } => { Term::Apply { function, argument } => {
self.step_and_maybe_spend(StepKind::Apply)?; self.step_and_maybe_spend(StepKind::Apply)?;
self.compute( self.stack.push(MachineStep::Compute(
Context::FrameApplyArg(env.clone(), *argument.clone(), Box::new(context)), Context::FrameApplyArg(env.clone(), *argument.clone(), Box::new(context)),
env, env,
function, *function.clone(),
) ));
} }
Term::Constant(x) => { Term::Constant(x) => {
self.step_and_maybe_spend(StepKind::Constant)?; self.step_and_maybe_spend(StepKind::Constant)?;
self.return_compute(context, Value::Con(x.clone())) self.stack
.push(MachineStep::Return(context, Value::Con(x.clone())));
} }
Term::Force(body) => { Term::Force(body) => {
self.step_and_maybe_spend(StepKind::Force)?; self.step_and_maybe_spend(StepKind::Force)?;
self.compute(Context::FrameForce(Box::new(context)), env, body) self.stack.push(MachineStep::Compute(
Context::FrameForce(Box::new(context)),
env,
*body.clone(),
));
} }
Term::Error => Err(Error::EvaluationFailure), Term::Error => return Err(Error::EvaluationFailure),
Term::Builtin(fun) => { Term::Builtin(fun) => {
self.step_and_maybe_spend(StepKind::Builtin)?; self.step_and_maybe_spend(StepKind::Builtin)?;
let runtime: BuiltinRuntime = (*fun).into(); let runtime: BuiltinRuntime = (*fun).into();
self.return_compute( self.stack.push(MachineStep::Return(
context, context,
Value::Builtin { Value::Builtin {
fun: *fun, fun: *fun,
term: term.clone(), term: term.clone(),
runtime, runtime,
}, },
) ));
}
} }
};
Ok(())
} }
fn return_compute( fn return_compute(&mut self, context: Context, value: Value) -> Result<(), Error> {
&mut self,
context: Context,
value: Value,
) -> Result<Term<NamedDeBruijn>, Error> {
match context { match context {
Context::FrameApplyFun(function, ctx) => self.apply_evaluate(*ctx, function, value), Context::FrameApplyFun(function, ctx) => self.apply_evaluate(*ctx, function, value)?,
Context::FrameApplyArg(arg_var_env, arg, ctx) => { Context::FrameApplyArg(arg_var_env, arg, ctx) => {
self.compute(Context::FrameApplyFun(value, ctx), arg_var_env, &arg) self.stack.push(MachineStep::Compute(
Context::FrameApplyFun(value, ctx),
arg_var_env,
arg,
));
} }
Context::FrameForce(ctx) => self.force_evaluate(*ctx, value), Context::FrameForce(ctx) => self.force_evaluate(*ctx, value)?,
Context::NoFrame => { Context::NoFrame => {
if self.unbudgeted_steps[7] > 0 { if self.unbudgeted_steps[7] > 0 {
self.spend_unbudgeted_steps()?; self.spend_unbudgeted_steps()?;
@ -128,9 +178,11 @@ impl Machine {
let term = self.discharge_value(value); let term = self.discharge_value(value);
Ok(term) self.stack.push(MachineStep::Done(term));
}
} }
};
Ok(())
} }
fn discharge_value(&mut self, value: Value) -> Term<NamedDeBruijn> { fn discharge_value(&mut self, value: Value) -> Term<NamedDeBruijn> {
@ -197,13 +249,13 @@ impl Machine {
rec(0, term, self, &env) rec(0, term, self, &env)
} }
fn force_evaluate( fn force_evaluate(&mut self, context: Context, value: Value) -> Result<(), Error> {
&mut self,
context: Context,
value: Value,
) -> Result<Term<NamedDeBruijn>, Error> {
match value { match value {
Value::Delay(body, env) => self.compute(context, env, &body), Value::Delay(body, env) => {
self.stack.push(MachineStep::Compute(context, env, body));
Ok(())
}
Value::Builtin { Value::Builtin {
fun, fun,
term, term,
@ -216,7 +268,9 @@ impl Machine {
let res = self.eval_builtin_app(fun, force_term, runtime)?; let res = self.eval_builtin_app(fun, force_term, runtime)?;
self.return_compute(context, res) self.stack.push(MachineStep::Return(context, res));
Ok(())
} else { } else {
Err(Error::BuiltinTermArgumentExpected(force_term)) Err(Error::BuiltinTermArgumentExpected(force_term))
} }
@ -230,14 +284,16 @@ impl Machine {
context: Context, context: Context,
function: Value, function: Value,
argument: Value, argument: Value,
) -> Result<Term<NamedDeBruijn>, Error> { ) -> Result<(), Error> {
match function { match function {
Value::Lambda { body, env, .. } => { Value::Lambda { body, env, .. } => {
let mut e = env; let mut e = env;
e.push(argument); e.push(argument);
self.compute(context, e, &body) self.stack.push(MachineStep::Compute(context, e, body));
Ok(())
} }
Value::Builtin { Value::Builtin {
fun, fun,
@ -256,7 +312,9 @@ impl Machine {
let res = self.eval_builtin_app(fun, t, runtime)?; let res = self.eval_builtin_app(fun, t, runtime)?;
self.return_compute(context, res) self.stack.push(MachineStep::Return(context, res));
Ok(())
} else { } else {
Err(Error::UnexpectedBuiltinTermArgument(t)) Err(Error::UnexpectedBuiltinTermArgument(t))
} }

View File

@ -28,6 +28,8 @@ pub enum Error {
BuiltinTermArgumentExpected(Term<NamedDeBruijn>), BuiltinTermArgumentExpected(Term<NamedDeBruijn>),
#[error("Unable to unlift value because it is not a constant:\n\n{0:#?}")] #[error("Unable to unlift value because it is not a constant:\n\n{0:#?}")]
NotAConstant(Value), NotAConstant(Value),
#[error("The evaluation never reached a final state")]
MachineNeverReachedDone,
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]