Making progress on using interning in gen_uplc

Done interning for uniqueness. Now to fix the static optimization

Remove unused function

Fixing issues. Have a few remaining tests
This commit is contained in:
microproofs 2024-09-09 17:29:15 -04:00
parent 0905146140
commit 7c52094b15
No known key found for this signature in database
GPG Key ID: 14F93C84DE6AFD17
3 changed files with 756 additions and 433 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
use super::{ use super::{
air::ExpectLevel, air::ExpectLevel,
tree::{AirMsg, AirTree, TreePath}, interner::AirInterner,
tree::{AirMsg, AirTree},
}; };
use crate::{ use crate::{
ast::{ ast::{
@ -17,7 +18,7 @@ use crate::{
}; };
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use itertools::{Itertools, Position}; use itertools::{Itertools, Position};
use std::{collections::HashMap, ops::Deref, rc::Rc}; use std::{ops::Deref, rc::Rc};
use uplc::{ use uplc::{
ast::{Constant as UplcConstant, Name, Term, Type as UplcType}, ast::{Constant as UplcConstant, Name, Term, Type as UplcType},
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER}, builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
@ -41,6 +42,7 @@ pub const CONSTR_NOT_EMPTY: &str = "__CONSTR_NOT_EMPTY";
pub const INCORRECT_BOOLEAN: &str = "__INCORRECT_BOOLEAN"; pub const INCORRECT_BOOLEAN: &str = "__INCORRECT_BOOLEAN";
pub const INCORRECT_CONSTR: &str = "__INCORRECT_CONSTR"; pub const INCORRECT_CONSTR: &str = "__INCORRECT_CONSTR";
pub const CONSTR_INDEX_MISMATCH: &str = "__CONSTR_INDEX_MISMATCH"; pub const CONSTR_INDEX_MISMATCH: &str = "__CONSTR_INDEX_MISMATCH";
pub const DISCARDED: &str = "_";
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum CodeGenFunction { pub enum CodeGenFunction {
@ -421,21 +423,17 @@ pub fn is_recursive_function_call<'a>(
pub fn identify_recursive_static_params( pub fn identify_recursive_static_params(
air_tree: &mut AirTree, air_tree: &mut AirTree,
tree_path: &TreePath,
func_params: &[String], func_params: &[String],
func_key: &(FunctionAccessKey, String), func_key: &(FunctionAccessKey, String),
function_calls_and_usage: &mut (usize, usize), function_calls_and_usage: &mut (usize, usize),
shadowed_parameters: &mut HashMap<String, TreePath>,
potential_recursive_statics: &mut Vec<String>, potential_recursive_statics: &mut Vec<String>,
) { ) {
let variant = &func_key.1; let variant = &func_key.1;
let func_key = &func_key.0; let func_key = &func_key.0;
// Find whether any of the potential recursive statics get shadowed (because even if we pass in the same referenced name, it might not be static)
for introduced_variable in find_introduced_variables(air_tree) { // Now all variables can't be shadowed at this stage due to interning
if potential_recursive_statics.contains(&introduced_variable) {
shadowed_parameters.insert(introduced_variable, tree_path.clone());
}
}
// Otherwise, if this is a recursive call site, disqualify anything that is different (or the same, but shadowed) // Otherwise, if this is a recursive call site, disqualify anything that is different (or the same, but shadowed)
if let (true, Some(args)) = is_recursive_function_call(air_tree, func_key, variant) { if let (true, Some(args)) = is_recursive_function_call(air_tree, func_key, variant) {
for (param, arg) in func_params.iter().zip(args) { for (param, arg) in func_params.iter().zip(args) {
@ -446,18 +444,9 @@ pub fn identify_recursive_static_params(
// Check if we pass something different in this recursive call site // Check if we pass something different in this recursive call site
// by different, we mean // by different, we mean
// - a variable that is bound to a different name // - a variable that is bound to a different name
// - a variable with the same name, but that was shadowed in an ancestor scope
// - any other type of expression // - any other type of expression
let param_is_different = match arg { let param_is_different = match arg {
AirTree::Var { name, .. } => { AirTree::Var { name, .. } => name != param || false,
// "shadowed in an ancestor scope" means "the definition scope is a prefix of our scope"
name != param
|| if let Some(p) = shadowed_parameters.get(param) {
p.common_ancestor(tree_path) == *p
} else {
false
}
}
_ => true, _ => true,
}; };
// If so, then we disqualify this parameter from being a recursive static parameter // If so, then we disqualify this parameter from being a recursive static parameter
@ -499,17 +488,14 @@ pub fn modify_self_calls(
// identify which parameters are recursively nonstatic (i.e. get modified before the self-call) // identify which parameters are recursively nonstatic (i.e. get modified before the self-call)
// TODO: this would be a lot simpler if each `Var`, `Let`, function argument, etc. had a unique identifier // TODO: this would be a lot simpler if each `Var`, `Let`, function argument, etc. had a unique identifier
// rather than just a name; this would let us track if the Var passed to itself was the same value as the method argument // rather than just a name; this would let us track if the Var passed to itself was the same value as the method argument
let mut shadowed_parameters: HashMap<String, TreePath> = HashMap::new();
let mut calls_and_var_usage = (0, 0); let mut calls_and_var_usage = (0, 0);
body.traverse_tree_with( body.traverse_tree_with(
&mut |air_tree: &mut AirTree, tree_path| { &mut |air_tree: &mut AirTree, _tree_path| {
identify_recursive_static_params( identify_recursive_static_params(
air_tree, air_tree,
tree_path,
func_params, func_params,
&(func_key.clone(), variant.clone()), &(func_key.clone(), variant.clone()),
&mut calls_and_var_usage, &mut calls_and_var_usage,
&mut shadowed_parameters,
&mut potential_recursive_statics, &mut potential_recursive_statics,
); );
}, },
@ -1661,19 +1647,26 @@ pub fn get_list_elements_len_and_tail(
} }
} }
pub fn cast_validator_args(term: Term<Name>, arguments: &[TypedArg]) -> Term<Name> { pub fn cast_validator_args(
term: Term<Name>,
arguments: &[TypedArg],
interner: &AirInterner,
) -> Term<Name> {
let mut term = term; let mut term = term;
for arg in arguments.iter().rev() { for arg in arguments.iter().rev() {
let name = arg
.arg_name
.get_variable_name()
.map(|arg| interner.lookup_interned(&arg.to_string()))
.unwrap_or_else(|| "_".to_string());
if !matches!(arg.tipo.get_uplc_type(), Some(UplcType::Data) | None) { if !matches!(arg.tipo.get_uplc_type(), Some(UplcType::Data) | None) {
term = term term = term
.lambda(arg.arg_name.get_variable_name().unwrap_or("_")) .lambda(&name)
.apply(known_data_to_type( .apply(known_data_to_type(Term::var(&name), &arg.tipo));
Term::var(arg.arg_name.get_variable_name().unwrap_or("_")),
&arg.tipo,
));
} }
term = term.lambda(arg.arg_name.get_variable_name().unwrap_or("_")) term = term.lambda(name)
} }
term term
} }
@ -1751,3 +1744,89 @@ pub fn get_line_columns_by_span(
.line_and_column_number(span.start) .line_and_column_number(span.start)
.expect("Out of bounds span") .expect("Out of bounds span")
} }
pub fn introduce_pattern(interner: &mut AirInterner, pattern: &TypedPattern) {
match pattern {
Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::Discard { .. } => (),
Pattern::Var { name, .. } => {
interner.intern(name.clone());
}
Pattern::Assign { name, pattern, .. } => {
interner.intern(name.clone());
introduce_pattern(interner, pattern);
}
Pattern::List { elements, tail, .. } => {
elements.iter().for_each(|element| {
introduce_pattern(interner, element);
});
tail.iter().for_each(|tail| {
introduce_pattern(interner, tail);
});
}
Pattern::Constructor {
arguments: elems, ..
} => {
elems.iter().for_each(|element| {
introduce_pattern(interner, &element.value);
});
}
Pattern::Tuple { elems, .. } => {
elems.iter().for_each(|element| {
introduce_pattern(interner, element);
});
}
Pattern::Pair { fst, snd, .. } => {
introduce_pattern(interner, fst);
introduce_pattern(interner, snd);
}
}
}
pub fn pop_pattern(interner: &mut AirInterner, pattern: &TypedPattern) {
match pattern {
Pattern::Int { .. } | Pattern::ByteArray { .. } | Pattern::Discard { .. } => (),
Pattern::Var { name, .. } => {
interner.pop_text(name.clone());
}
Pattern::Assign { name, pattern, .. } => {
interner.pop_text(name.clone());
pop_pattern(interner, pattern);
}
Pattern::List { elements, tail, .. } => {
elements.iter().for_each(|element| {
pop_pattern(interner, element);
});
tail.iter().for_each(|tail| {
pop_pattern(interner, tail);
});
}
Pattern::Constructor {
arguments: elems, ..
} => {
elems.iter().for_each(|element| {
pop_pattern(interner, &element.value);
});
}
Pattern::Tuple { elems, .. } => {
elems.iter().for_each(|element| {
pop_pattern(interner, element);
});
}
Pattern::Pair { fst, snd, .. } => {
pop_pattern(interner, fst);
pop_pattern(interner, snd);
}
}
}
pub fn introduce_name(interner: &mut AirInterner, name: &String) -> String {
interner.intern(name.clone());
interner.lookup_interned(&name)
}

View File

@ -0,0 +1,57 @@
use std::collections::HashMap;
use vec1::{vec1, Vec1};
#[derive(Clone)]
pub struct AirInterner {
identifiers: HashMap<String, Vec1<usize>>,
current: usize,
}
impl Default for AirInterner {
fn default() -> Self {
Self::new()
}
}
/// Interner that uses previous uniques to prevent future unique collisions
/// when performing optimizations
impl AirInterner {
pub fn new() -> Self {
Self {
identifiers: HashMap::new(),
current: 0,
}
}
pub fn intern(&mut self, text: String) {
if let Some(u) = self.identifiers.get_mut(&text) {
u.push(self.current);
self.current += 1;
} else {
self.identifiers.insert(text, vec1!(self.current));
self.current += 1;
}
}
pub fn pop_text(&mut self, text: String) {
if let Some(mut u) = self.identifiers.remove(&text) {
if u.len() != 1 {
u.pop().unwrap();
self.identifiers.insert(text, u);
}
} else {
unreachable!("Looking up a missing text: {}", text);
}
}
pub fn lookup_interned(&self, text: &String) -> String {
if let Some(u) = self.identifiers.get(text) {
format!("{}_id_{}", text, *u.last())
} else {
unreachable!("Looking up a missing text: {}", text);
}
}
}