implement optimizations

-Force usage on builtins
-Inline vars used once
-Lambdas that use a var as an arg
-Lambdas that use a const as an arg
This commit is contained in:
Kasey White 2023-02-01 20:50:51 -05:00 committed by Lucas
parent 88ce8ba8b9
commit fdf89b7326
7 changed files with 286 additions and 2 deletions

1
Cargo.lock generated
View File

@ -2480,6 +2480,7 @@ dependencies = [
"cryptoxide", "cryptoxide",
"flat-rs", "flat-rs",
"hex", "hex",
"indexmap",
"itertools", "itertools",
"pallas-addresses", "pallas-addresses",
"pallas-codec", "pallas-codec",

View File

@ -1587,6 +1587,7 @@ impl<'a> CodeGenerator<'a> {
); );
} }
} }
// TODO: Check constr for assert on all cases
constr @ Pattern::Constructor { .. } => { constr @ Pattern::Constructor { .. } => {
if matches!(assignment_properties.kind, AssignmentKind::Assert) if matches!(assignment_properties.kind, AssignmentKind::Assert)
&& assignment_properties.value_is_data && assignment_properties.value_is_data

View File

@ -30,6 +30,7 @@ serde_json = "1.0.85"
strum = "0.24.1" strum = "0.24.1"
strum_macros = "0.24.3" strum_macros = "0.24.3"
itertools = "0.10.5" itertools = "0.10.5"
indexmap = "1.9.2"
[dev-dependencies] [dev-dependencies]
hex = "0.4.3" hex = "0.4.3"

View File

@ -1,5 +1,6 @@
use std::{ use std::{
fmt::{self, Display}, fmt::{self, Display},
hash::{self, Hash},
rc::Rc, rc::Rc,
}; };
@ -276,12 +277,19 @@ impl Display for Type {
/// A Name containing it's parsed textual representation /// A Name containing it's parsed textual representation
/// and a unique id from string interning. The Name's text is /// and a unique id from string interning. The Name's text is
/// interned during parsing. /// interned during parsing.
#[derive(Debug, Clone)] #[derive(Debug, Clone, Eq)]
pub struct Name { pub struct Name {
pub text: String, pub text: String,
pub unique: Unique, pub unique: Unique,
} }
impl hash::Hash for Name {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.text.hash(state);
self.unique.hash(state);
}
}
impl PartialEq for Name { impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.unique == other.unique self.unique == other.unique
@ -326,7 +334,7 @@ impl Display for Unique {
/// Similar to `Name` but for Debruijn indices. /// Similar to `Name` but for Debruijn indices.
/// `Name` is replaced by `NamedDebruijn` when converting /// `Name` is replaced by `NamedDebruijn` when converting
/// program to it's debruijn form. /// program to it's debruijn form.
#[derive(Debug, Clone)] #[derive(Debug, Clone, Eq)]
pub struct NamedDeBruijn { pub struct NamedDeBruijn {
pub text: String, pub text: String,
pub index: DeBruijn, pub index: DeBruijn,

View File

@ -7,6 +7,7 @@ pub mod parser;
mod pretty; mod pretty;
pub mod program_builder; pub mod program_builder;
pub mod tx; pub mod tx;
pub mod optimize;
pub use pallas_codec::utils::KeyValuePairs; pub use pallas_codec::utils::KeyValuePairs;
pub use pallas_crypto::hash::Hash; pub use pallas_crypto::hash::Hash;

View File

@ -0,0 +1,7 @@
use crate::ast::{Name, Program};
pub mod shrinker;
pub fn aiken_optimize(term: Program<Name>) -> Program<Name> {
term.builtin_force_reduce().lambda_reduce().inline_reduce()
}

View File

@ -0,0 +1,265 @@
use std::rc::Rc;
use indexmap::IndexMap;
use itertools::Itertools;
use crate::{
ast::{builder::apply_wrap, Name, Program, Term},
builtins::DefaultFunction,
};
// use crate::builtins::{DefaultFunction};
#[derive(Eq, Hash, PartialEq, Clone)]
pub struct Occurrence {
name: Rc<Name>,
lambda_count: usize,
}
impl Program<Name> {
pub fn lambda_reduce(self) -> Program<Name> {
let mut term = self.term.clone();
lambda_reduce(&mut term);
Program {
version: self.version,
term,
}
}
pub fn builtin_force_reduce(self) -> Program<Name> {
let mut term = self.term.clone();
let mut builtin_map = IndexMap::new();
builtin_force_reduce(&mut term, &mut builtin_map);
for default_func_index in builtin_map.keys().sorted().cloned() {
let default_func: DefaultFunction = default_func_index.try_into().unwrap();
term = apply_wrap(
Term::Lambda {
parameter_name: Name {
text: format!("__{}_wrapped", default_func.aiken_name()),
unique: 0.into(),
}
.into(),
body: term.into(),
},
if default_func.force_count() == 1 {
Term::Builtin(default_func).force_wrap()
} else {
Term::Builtin(default_func).force_wrap().force_wrap()
},
);
}
Program {
version: self.version,
term,
}
}
pub fn inline_reduce(self) -> Program<Name> {
let mut term = self.term.clone();
inline_reduce(&mut term);
Program {
version: self.version,
term,
}
}
}
fn builtin_force_reduce(term: &mut Term<Name>, builtin_map: &mut IndexMap<u8, ()>) {
match term {
Term::Force(f) => {
let f = Rc::make_mut(f);
match f {
Term::Force(inner_f) => {
if let Term::Builtin(func) = inner_f.as_ref() {
builtin_map.insert(*func as u8, ());
*term = Term::Var(
Name {
text: format!("__{}_wrapped", func.aiken_name()),
unique: 0.into(),
}
.into(),
);
return;
}
}
Term::Builtin(func) => {
builtin_map.insert(*func as u8, ());
*term = Term::Var(
Name {
text: format!("__{}", func.aiken_name()),
unique: 0.into(),
}
.into(),
);
return;
}
_ => {}
}
builtin_force_reduce(f, builtin_map);
}
Term::Delay(d) => {
let d = Rc::make_mut(d);
builtin_force_reduce(d, builtin_map);
}
Term::Lambda { body, .. } => {
let body = Rc::make_mut(body);
builtin_force_reduce(body, builtin_map);
}
Term::Apply { function, argument } => {
let func = Rc::make_mut(function);
builtin_force_reduce(func, builtin_map);
let arg = Rc::make_mut(argument);
builtin_force_reduce(arg, builtin_map);
}
_ => {}
}
}
fn inline_reduce(term: &mut Term<Name>) {
match term {
Term::Delay(d) => {
let d = Rc::make_mut(d);
inline_reduce(d);
}
Term::Lambda { body, .. } => {
let body = Rc::make_mut(body);
inline_reduce(body);
}
Term::Apply { function, argument } => {
let arg = Rc::make_mut(argument);
inline_reduce(arg);
let func = Rc::make_mut(function);
inline_reduce(func);
if let Term::Lambda {
parameter_name,
body,
} = func
{
let mut occurrences = 0;
var_occurrences(body, parameter_name.clone(), &mut occurrences);
if occurrences <= 1 {
*term = substitute_term(body.as_ref(), parameter_name.clone(), argument);
}
}
}
Term::Force(f) => {
let f = Rc::make_mut(f);
inline_reduce(f);
}
_ => {}
}
}
fn var_occurrences(term: &Term<Name>, search_for: Rc<Name>, occurrences: &mut usize) {
match term {
Term::Var(name) => {
if name.clone() == search_for {
*occurrences += 1;
}
}
Term::Delay(body) => {
var_occurrences(body.as_ref(), search_for, occurrences);
}
Term::Lambda {
parameter_name,
body,
} => {
if parameter_name.clone() != search_for {
var_occurrences(body.as_ref(), search_for, occurrences);
}
}
Term::Apply { function, argument } => {
var_occurrences(function.as_ref(), search_for.clone(), occurrences);
var_occurrences(argument.as_ref(), search_for, occurrences);
}
Term::Force(x) => {
var_occurrences(x.as_ref(), search_for, occurrences);
}
_ => {}
}
}
fn lambda_reduce(term: &mut Term<Name>) {
match term {
Term::Apply { function, argument } => {
let func = Rc::make_mut(function);
lambda_reduce(func);
let arg = Rc::make_mut(argument);
lambda_reduce(arg);
if let Term::Lambda {
parameter_name,
body,
} = func
{
if let replace_term @ (Term::Var(_) | Term::Constant(_)) = argument.as_ref() {
let body = Rc::make_mut(body);
lambda_reduce(body);
*body = substitute_term(body, parameter_name.clone(), replace_term);
}
}
}
Term::Delay(d) => {
let d = Rc::make_mut(d);
lambda_reduce(d);
}
Term::Lambda { body, .. } => {
let body = Rc::make_mut(body);
lambda_reduce(body);
}
Term::Force(f) => {
let f = Rc::make_mut(f);
lambda_reduce(f);
}
_ => {}
}
}
fn substitute_term(term: &Term<Name>, original: Rc<Name>, replace_with: &Term<Name>) -> Term<Name> {
match term {
Term::Var(name) => {
if name.clone() == original {
replace_with.clone()
} else {
Term::Var(name.clone())
}
}
Term::Delay(body) => {
Term::Delay(substitute_term(body.as_ref(), original, replace_with).into())
}
Term::Lambda {
parameter_name,
body,
} => {
if parameter_name.clone() != original {
Term::Lambda {
parameter_name: parameter_name.clone(),
body: Rc::new(substitute_term(body.as_ref(), original, replace_with)),
}
} else {
Term::Lambda {
parameter_name: parameter_name.clone(),
body: body.clone(),
}
}
}
Term::Apply { function, argument } => Term::Apply {
function: Rc::new(substitute_term(
function.as_ref(),
original.clone(),
replace_with,
)),
argument: Rc::new(substitute_term(argument.as_ref(), original, replace_with)),
},
Term::Force(x) => Term::Force(Rc::new(substitute_term(x.as_ref(), original, replace_with))),
x => x.clone(),
}
}