checkpoint

This commit is contained in:
microproofs 2024-02-29 17:34:30 -05:00 committed by Kasey
parent 4e0aaf970f
commit c6ef37cc5c
5 changed files with 240 additions and 166 deletions

View File

@ -420,10 +420,6 @@ impl Unique {
pub fn increment(&mut self) { pub fn increment(&mut self) {
self.0 += 1; self.0 += 1;
} }
pub fn large_offset(&mut self) {
self.0 += 5_000_000;
}
} }
impl From<isize> for Unique { impl From<isize> for Unique {

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
ast::{Constant, Name, Term, Type, Unique}, ast::{Constant, Name, Term, Type},
builtins::DefaultFunction, builtins::DefaultFunction,
}; };
use pallas::ledger::primitives::alonzo::PlutusData; use pallas::ledger::primitives::alonzo::PlutusData;
@ -464,31 +464,10 @@ impl Term<Name> {
} }
} }
pub fn lambda_unique(self, parameter_name: impl ToString, unique: Unique) -> Self {
Term::Lambda {
parameter_name: Name {
text: parameter_name.to_string(),
unique,
}
.into(),
body: self.into(),
}
}
pub fn var(name: impl ToString) -> Self { pub fn var(name: impl ToString) -> Self {
Term::Var(Name::text(name).into()) Term::Var(Name::text(name).into())
} }
pub fn var_unique(name: impl ToString, unique: Unique) -> Self {
Term::Var(
Name {
text: name.to_string(),
unique,
}
.into(),
)
}
// Misc. // Misc.
pub fn constr_fields_exposer(self) -> Self { pub fn constr_fields_exposer(self) -> Self {
self.lambda(CONSTR_FIELDS_EXPOSER).apply( self.lambda(CONSTR_FIELDS_EXPOSER).apply(

View File

@ -1,23 +1,28 @@
use crate::ast::{Name, Program}; use crate::ast::{Name, Program};
mod interner;
pub mod shrinker; pub mod shrinker;
pub fn aiken_optimize_and_intern(program: Program<Name>) -> Program<Name> { pub fn aiken_optimize_and_intern(program: Program<Name>) -> Program<Name> {
let (program, mut current_unique) = program.builtin_force_reducer(); let w = program
current_unique.large_offset(); .builtin_force_reducer()
program
.lambda_reducer() .lambda_reducer()
.inline_reducer() .inline_reducer()
.lambda_reducer() .lambda_reducer()
.inline_reducer() .inline_reducer()
.force_delay_reducer() .force_delay_reducer()
.cast_data_reducer() .cast_data_reducer()
.convert_arithmetic_ops() .convert_arithmetic_ops();
.builtin_curry_reducer(&mut current_unique)
.lambda_reducer() // println!("{:#?}", w);
.inline_reducer()
.builtin_curry_reducer(&mut current_unique) let x = w.builtin_curry_reducer();
.lambda_reducer()
.inline_reducer() // println!("{:#?}", x);
let y = x.lambda_reducer().inline_reducer().builtin_curry_reducer();
// println!("{:#?}", y);
y.lambda_reducer().inline_reducer()
} }

View File

@ -0,0 +1,97 @@
use std::{collections::HashMap, rc::Rc};
use crate::ast::{Name, Program, Term, Unique};
#[derive(Eq, Hash, PartialEq, Clone)]
pub struct InternKey {
name: String,
previous_unique: Unique,
}
pub struct CodeGenInterner {
identifiers: HashMap<InternKey, Unique>,
current: Unique,
}
impl Default for CodeGenInterner {
fn default() -> Self {
Self::new()
}
}
/// Interner that uses previous uniques to prevent future unique collisions
/// when performing optimizations
impl CodeGenInterner {
pub fn new() -> Self {
Self {
identifiers: HashMap::new(),
current: Unique::new(0),
}
}
pub fn program(&mut self, program: &mut Program<Name>) {
self.term(&mut program.term);
}
pub fn term(&mut self, term: &mut Term<Name>) {
match term {
Term::Var(name) => {
let name = Rc::make_mut(name);
name.unique = self.intern(name.text.clone(), name.unique);
}
Term::Delay(term) => self.term(Rc::make_mut(term)),
Term::Lambda {
parameter_name,
body,
} => {
let parameter_name = Rc::make_mut(parameter_name);
parameter_name.unique =
self.intern(parameter_name.text.clone(), parameter_name.unique);
self.term(Rc::make_mut(body));
}
Term::Apply { function, argument } => {
self.term(Rc::make_mut(function));
self.term(Rc::make_mut(argument));
}
Term::Constant(_) => (),
Term::Force(term) => self.term(Rc::make_mut(term)),
Term::Error => (),
Term::Builtin(_) => (),
Term::Constr { fields, .. } => {
for field in fields {
self.term(field);
}
}
Term::Case { constr, branches } => {
self.term(Rc::make_mut(constr));
for branch in branches {
self.term(branch);
}
}
}
}
pub fn intern(&mut self, text: String, previous_unique: Unique) -> Unique {
let key = InternKey {
name: text,
previous_unique,
};
if let Some(u) = self.identifiers.get(&key) {
*u
} else {
let unique = self.current;
self.identifiers.insert(key, self.current_unique());
self.current.increment();
unique
}
}
pub fn current_unique(&self) -> Unique {
self.current
}
}

View File

@ -6,11 +6,12 @@ use itertools::Itertools;
use pallas::ledger::primitives::babbage::{BigInt, PlutusData}; use pallas::ledger::primitives::babbage::{BigInt, PlutusData};
use crate::{ use crate::{
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type, Unique}, ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
builtins::DefaultFunction, builtins::DefaultFunction,
parser::interner::Interner,
}; };
use super::interner::CodeGenInterner;
#[derive(Eq, Hash, PartialEq, Clone, Debug, PartialOrd)] #[derive(Eq, Hash, PartialEq, Clone, Debug, PartialOrd)]
pub enum ScopePath { pub enum ScopePath {
FUNC, FUNC,
@ -310,6 +311,7 @@ pub struct CurriedNode {
term: Term<Name>, term: Term<Name>,
} }
#[derive(PartialEq, Clone, Debug)]
pub struct UplcNode { pub struct UplcNode {
applied_id: usize, applied_id: usize,
curried_id: usize, curried_id: usize,
@ -640,6 +642,22 @@ impl CurriedArgs {
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn is_flipped(&self, path: &BuiltinArgs) -> bool {
match (self, path) {
(CurriedArgs::TwoArgs { fst_args, .. }, BuiltinArgs::TwoArgsAnyOrder { fst, snd }) => {
if fst_args.iter().any(|item| item.term == fst.1) {
false
} else {
fst_args.iter().any(|item| match &snd {
Some(snd) => item.term == snd.1,
None => false,
})
}
}
_ => false,
}
}
} }
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub struct CurriedBuiltin { pub struct CurriedBuiltin {
@ -660,6 +678,10 @@ impl CurriedBuiltin {
pub fn get_id_args(&self, path: &BuiltinArgs) -> Option<Vec<UplcNode>> { pub fn get_id_args(&self, path: &BuiltinArgs) -> Option<Vec<UplcNode>> {
self.args.get_id_args(path) self.args.get_id_args(path)
} }
pub fn is_flipped(&self, path: &BuiltinArgs) -> bool {
self.args.is_flipped(path)
}
} }
impl Term<Name> { impl Term<Name> {
@ -798,7 +820,7 @@ impl Program<Name> {
}) })
} }
pub fn builtin_force_reducer(self) -> (Self, Unique) { pub fn builtin_force_reducer(self) -> Self {
let mut builtin_map = IndexMap::new(); let mut builtin_map = IndexMap::new();
let program = self.traverse_uplc_with(&mut |_id, term, _arg_stack, _scope| { let program = self.traverse_uplc_with(&mut |_id, term, _arg_stack, _scope| {
@ -850,16 +872,13 @@ impl Program<Name> {
term, term,
}; };
let mut interner = Interner::new(); let mut interner = CodeGenInterner::new();
interner.program(&mut program); interner.program(&mut program);
let program = Program::<NamedDeBruijn>::try_from(program).unwrap(); let program = Program::<NamedDeBruijn>::try_from(program).unwrap();
( Program::<Name>::try_from(program).unwrap()
Program::<Name>::try_from(program).unwrap(),
interner.current_unique(),
)
} }
pub fn inline_reducer(self) -> Self { pub fn inline_reducer(self) -> Self {
@ -1147,15 +1166,17 @@ impl Program<Name> {
}) })
} }
pub fn builtin_curry_reducer(self, current: &mut Unique) -> Self { pub fn builtin_curry_reducer(self) -> Self {
let mut curried_terms = vec![]; let mut curried_terms = vec![];
let mut id_mapped_curry_terms: IndexMap<CurriedName, (Scope, Term<Name>, bool)> = let mut id_mapped_curry_terms: IndexMap<CurriedName, (Scope, Term<Name>, bool)> =
IndexMap::new(); IndexMap::new();
let mut curry_applied_ids = vec![]; let mut curry_applied_ids = vec![];
let mut scope_mapped_to_term: IndexMap<Scope, Vec<(CurriedName, Term<Name>)>> = let mut scope_mapped_to_term: IndexMap<Scope, Vec<(CurriedName, Term<Name>)>> =
IndexMap::new(); IndexMap::new();
let mut flipped_terms: IndexMap<Scope, bool> = IndexMap::new();
let mut final_ids: IndexMap<Vec<usize>, ()> = IndexMap::new(); let mut final_ids: IndexMap<Vec<usize>, ()> = IndexMap::new();
let mut unique_map: IndexMap<String, Unique> = IndexMap::new();
let step_a = self.traverse_uplc_with(&mut |_id, term, arg_stack, scope| match term { let step_a = self.traverse_uplc_with(&mut |_id, term, arg_stack, scope| match term {
Term::Builtin(func) => { Term::Builtin(func) => {
@ -1168,31 +1189,40 @@ impl Program<Name> {
let builtin_args = let builtin_args =
BuiltinArgs::args_from_arg_stack(arg_stack, is_order_agnostic); BuiltinArgs::args_from_arg_stack(arg_stack, is_order_agnostic);
// Get upper scope of the function plus args
// So for example if the scope is [.., ARG, ARG, FUNC]
// we want to pop off the last 3 to get the scope right above the function applications
// First we see if we have already curried this builtin before // First we see if we have already curried this builtin before
let mut id_vec = if let Some(curried_builtin) = curried_terms let mut id_vec = if let Some((index, _)) =
.iter_mut() curried_terms.iter_mut().find_position(
.find(|curried_term: &&mut CurriedBuiltin| curried_term.func == *func) |curried_term: &&mut CurriedBuiltin| curried_term.func == *func,
{ ) {
// We found it the builtin was curried before // We found it the builtin was curried before
// So now we merge the new args into the existing curried builtin // So now we merge the new args into the existing curried builtin
*curried_builtin = (*curried_builtin)
.clone() let curried_builtin = curried_terms.remove(index);
.merge_node_by_path(builtin_args.clone());
let curried_builtin =
curried_builtin.merge_node_by_path(builtin_args.clone());
let Some(id_vec) = curried_builtin.get_id_args(&builtin_args) else { let Some(id_vec) = curried_builtin.get_id_args(&builtin_args) else {
unreachable!(); unreachable!();
}; };
flipped_terms
.insert(scope.clone(), curried_builtin.is_flipped(&builtin_args));
curried_terms.push(curried_builtin);
id_vec id_vec
} else { } else {
// Brand new buitlin so we add it to the list // Brand new buitlin so we add it to the list
curried_terms.push(builtin_args.clone().args_to_curried_args(*func)); let curried_builtin = builtin_args.clone().args_to_curried_args(*func);
builtin_args.get_id_args() let Some(id_vec) = curried_builtin.get_id_args(&builtin_args) else {
unreachable!();
};
curried_terms.push(curried_builtin);
id_vec
}; };
while let Some(node) = id_vec.pop() { while let Some(node) = id_vec.pop() {
@ -1201,8 +1231,6 @@ impl Program<Name> {
id_only_vec.push(node.curried_id); id_only_vec.push(node.curried_id);
let id_var_name = id_vec_function_to_var(&func.aiken_name(), &id_only_vec);
let curry_name = CurriedName { let curry_name = CurriedName {
func_name: func.aiken_name(), func_name: func.aiken_name(),
id_vec: id_only_vec, id_vec: id_only_vec,
@ -1214,33 +1242,19 @@ impl Program<Name> {
*map_scope = map_scope.common_ancestor(scope); *map_scope = map_scope.common_ancestor(scope);
*multi_occurrences = true; *multi_occurrences = true;
} else if id_vec.is_empty() { } else if id_vec.is_empty() {
unique_map.insert(id_var_name, *current);
current.increment();
id_mapped_curry_terms.insert( id_mapped_curry_terms.insert(
curry_name, curry_name,
(scope.clone(), Term::Builtin(*func).apply(node.term), false), (scope.clone(), Term::Builtin(*func).apply(node.term), false),
); );
} else { } else {
unique_map.insert(id_var_name, *current);
current.increment();
let var_name = id_vec_function_to_var( let var_name = id_vec_function_to_var(
&func.aiken_name(), &func.aiken_name(),
&id_vec.iter().map(|item| item.curried_id).collect_vec(), &id_vec.iter().map(|item| item.curried_id).collect_vec(),
); );
let unique = *unique_map.get(&var_name).unwrap_or(current);
id_mapped_curry_terms.insert( id_mapped_curry_terms.insert(
curry_name, curry_name,
( (scope.clone(), Term::var(var_name).apply(node.term), false),
scope.clone(),
Term::var_unique(var_name, unique).apply(node.term),
false,
),
); );
} }
} }
@ -1272,104 +1286,91 @@ impl Program<Name> {
} }
}); });
step_a.traverse_uplc_with(&mut |id, term, arg_stack, scope| match term { let mut step_b =
Term::Builtin(func) => { step_a.traverse_uplc_with(&mut |id, term, mut arg_stack, scope| match term {
if func.can_curry_builtin() && arg_stack.len() == func.arity() { Term::Builtin(func) => {
let Some(curried_builtin) = if func.can_curry_builtin() && arg_stack.len() == func.arity() {
curried_terms.iter().find(|curry| curry.func == *func) let Some(curried_builtin) =
else { curried_terms.iter().find(|curry| curry.func == *func)
return; else {
}; return;
};
let builtin_args = BuiltinArgs::args_from_arg_stack( if let Some(true) = flipped_terms.get(scope) {
arg_stack, arg_stack.reverse();
func.is_order_agnostic_builtin(),
);
let Some(mut id_vec) = curried_builtin.get_id_args(&builtin_args) else {
return;
};
while !id_vec.is_empty() {
let id_lookup = id_vec.iter().map(|item| item.curried_id).collect_vec();
if final_ids.contains_key(&id_lookup) {
break;
} }
id_vec.pop();
}
if id_vec.is_empty() { let builtin_args = BuiltinArgs::args_from_arg_stack(
return; arg_stack,
} func.is_order_agnostic_builtin(),
);
let name = id_vec_function_to_var( let Some(mut id_vec) = curried_builtin.get_id_args(&builtin_args) else {
&func.aiken_name(), return;
&id_vec.iter().map(|item| item.curried_id).collect_vec(), };
);
id_vec.iter().for_each(|item| { while !id_vec.is_empty() {
curry_applied_ids.push(item.applied_id); let id_lookup = id_vec.iter().map(|item| item.curried_id).collect_vec();
});
let unique = *unique_map.get(&name).unwrap(); if final_ids.contains_key(&id_lookup) {
break;
*term = Term::var_unique(name, unique);
}
}
Term::Apply { function, .. } => {
let id = id.unwrap();
if curry_applied_ids.contains(&id) {
*term = function.as_ref().clone();
}
if let Some(insert_list) = scope_mapped_to_term.remove(scope) {
for (key, val) in insert_list.into_iter().rev() {
let name = id_vec_function_to_var(&key.func_name, &key.id_vec);
let unique = *unique_map.get(&name).unwrap();
if var_occurrences(
term,
Name {
text: name.clone(),
unique,
} }
.into(), id_vec.pop();
)
.found
{
*term = term.clone().lambda_unique(name, unique).apply(val);
} }
if id_vec.is_empty() {
return;
}
let name = id_vec_function_to_var(
&func.aiken_name(),
&id_vec.iter().map(|item| item.curried_id).collect_vec(),
);
id_vec.iter().for_each(|item| {
curry_applied_ids.push(item.applied_id);
});
*term = Term::var(name);
} }
} }
} Term::Apply { function, .. } => {
Term::Constr { .. } => todo!(), let id = id.unwrap();
Term::Case { .. } => todo!(),
_ => {
if let Some(insert_list) = scope_mapped_to_term.remove(scope) {
for (key, val) in insert_list.into_iter().rev() {
let name = id_vec_function_to_var(&key.func_name, &key.id_vec);
let unique = *unique_map.get(&name).unwrap(); if curry_applied_ids.contains(&id) {
*term = function.as_ref().clone();
}
if var_occurrences( if let Some(insert_list) = scope_mapped_to_term.remove(scope) {
term, for (key, val) in insert_list.into_iter().rev() {
Name { let name = id_vec_function_to_var(&key.func_name, &key.id_vec);
text: name.clone(),
unique, if var_occurrences(term, Name::text(&name).into()).found {
*term = term.clone().lambda(name).apply(val);
} }
.into(),
)
.found
{
*term = term.clone().lambda_unique(name, unique).apply(val);
} }
} }
} }
} Term::Constr { .. } => todo!(),
}) Term::Case { .. } => todo!(),
_ => {
if let Some(insert_list) = scope_mapped_to_term.remove(scope) {
for (key, val) in insert_list.into_iter().rev() {
let name = id_vec_function_to_var(&key.func_name, &key.id_vec);
if var_occurrences(term, Name::text(&name).into()).found {
*term = term.clone().lambda(name).apply(val);
}
}
}
}
});
let mut interner = CodeGenInterner::new();
interner.program(&mut step_b);
step_b
} }
} }
@ -1776,7 +1777,7 @@ mod tests {
let expected: Program<NamedDeBruijn> = expected.try_into().unwrap(); let expected: Program<NamedDeBruijn> = expected.try_into().unwrap();
let (mut actual, _) = program.builtin_force_reducer(); let mut actual = program.builtin_force_reducer();
let mut interner = Interner::new(); let mut interner = Interner::new();
@ -1835,7 +1836,7 @@ mod tests {
let expected: Program<NamedDeBruijn> = expected.try_into().unwrap(); let expected: Program<NamedDeBruijn> = expected.try_into().unwrap();
let (mut actual, _) = program.builtin_force_reducer(); let mut actual = program.builtin_force_reducer();
let mut interner = Interner::new(); let mut interner = Interner::new();
@ -2001,13 +2002,9 @@ mod tests {
interner.program(&mut expected); interner.program(&mut expected);
let mut unique = interner.current_unique();
unique.large_offset();
let expected: Program<NamedDeBruijn> = expected.try_into().unwrap(); let expected: Program<NamedDeBruijn> = expected.try_into().unwrap();
let actual = program.builtin_curry_reducer(&mut unique); let actual = program.builtin_curry_reducer();
let actual: Program<NamedDeBruijn> = actual.try_into().unwrap(); let actual: Program<NamedDeBruijn> = actual.try_into().unwrap();