feat: Add multivalidator as an AIR tree opcode.

feat: Add uplc eval optimization
This commit is contained in:
microproofs 2024-07-24 09:59:12 -04:00
parent 06ac851458
commit dfce9c1d96
No known key found for this signature in database
GPG Key ID: 14F93C84DE6AFD17
8 changed files with 212 additions and 72 deletions

View File

@ -132,6 +132,46 @@ impl<'a> CodeGenerator<'a> {
let mut validator_args_tree =
self.check_validator_args(&fun.arguments, true, air_tree_fun, src_code, lines);
if let Some(other) = other_fun {
let mut air_tree_fun_other = self.build(&other.body, module_name, &[]);
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
let validator_args_tree_other = self.check_validator_args(
&other.arguments,
true,
air_tree_fun_other,
src_code,
lines,
);
let (spend, spend_name, mint, mint_name) =
if other.arguments.len() > fun.arguments.len() {
(
validator_args_tree_other,
other.name.clone(),
validator_args_tree,
fun.name.clone(),
)
} else {
(
validator_args_tree,
fun.name.clone(),
validator_args_tree_other,
other.name.clone(),
)
};
validator_args_tree = AirTree::multi_validator(mint_name, mint, spend_name, spend);
// Special Case with multi_validators
self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
}
validator_args_tree = AirTree::no_op(validator_args_tree);
let full_tree = self.hoist_functions_to_validator(validator_args_tree);
@ -142,48 +182,6 @@ impl<'a> CodeGenerator<'a> {
let mut term = self.uplc_code_gen(full_vec);
if let Some(other) = other_fun {
self.reset(false);
let mut air_tree_fun_other = self.build(&other.body, module_name, &[]);
air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing);
let mut validator_args_tree_other = self.check_validator_args(
&other.arguments,
true,
air_tree_fun_other,
src_code,
lines,
);
validator_args_tree_other = AirTree::no_op(validator_args_tree_other);
let full_tree_other = self.hoist_functions_to_validator(validator_args_tree_other);
// optimizations on air tree
let full_vec_other = full_tree_other.to_vec();
let other_term = self.uplc_code_gen(full_vec_other);
let (spend, spend_name, mint, mint_name) =
if other.arguments.len() > fun.arguments.len() {
(other_term, other.name.clone(), term, fun.name.clone())
} else {
(term, fun.name.clone(), other_term, other.name.clone())
};
// Special Case with multi_validators
self.special_functions
.use_function_uplc(CONSTR_FIELDS_EXPOSER.to_string());
self.special_functions
.use_function_uplc(CONSTR_INDEX_EXPOSER.to_string());
term = wrap_as_multi_validator(spend, mint, self.tracing, spend_name, mint_name);
}
term = cast_validator_args(term, params);
self.finalize(term)
@ -5700,6 +5698,24 @@ impl<'a> CodeGenerator<'a> {
}
Air::NoOp => None,
Air::MultiValidator {
two_arg_name,
three_arg_name,
} => {
let two_arg = arg_stack.pop().unwrap();
let three_arg = arg_stack.pop().unwrap();
let term = wrap_as_multi_validator(
three_arg,
two_arg,
self.tracing,
three_arg_name,
two_arg_name,
);
Some(term)
}
}
}
}

View File

@ -224,4 +224,8 @@ pub enum Air {
NoOp,
FieldsEmpty,
ListEmpty,
MultiValidator {
two_arg_name: String,
three_arg_name: String,
},
}

View File

@ -402,6 +402,12 @@ pub enum AirTree {
then: Box<AirTree>,
},
// End Expressions
MultiValidator {
two_arg_name: String,
two_arg: Box<AirTree>,
three_arg_name: String,
three_arg: Box<AirTree>,
},
}
impl AirTree {
@ -1001,26 +1007,19 @@ impl AirTree {
}
}
// pub fn hoist_over(mut self, next_exp: AirTree) -> AirTree {
// match &mut self {
// AirTree::Statement { hoisted_over, .. } => {
// assert!(hoisted_over.is_none());
// *hoisted_over = Some(next_exp.into());
// self
// }
// AirTree::Expression(_) => {
// unreachable!("Trying to hoist an expression onto an expression.")
// }
// AirTree::UnhoistedSequence(seq) => {
// let mut final_exp = next_exp;
// while let Some(assign) = seq.pop() {
// final_exp = assign.hoist_over(final_exp);
// }
// final_exp
// }
// }
// }
pub fn multi_validator(
two_arg_name: String,
two_arg: AirTree,
three_arg_name: String,
three_arg: AirTree,
) -> AirTree {
AirTree::MultiValidator {
two_arg_name,
two_arg: two_arg.into(),
three_arg_name,
three_arg: three_arg.into(),
}
}
pub fn expect_on_list() -> AirTree {
let list_var = AirTree::local_var("__list_to_check", list(data()));
@ -1595,6 +1594,20 @@ impl AirTree {
msg.create_air_vec(air_vec);
then.create_air_vec(air_vec);
}
AirTree::MultiValidator {
two_arg,
three_arg,
two_arg_name,
three_arg_name,
} => {
air_vec.push(Air::MultiValidator {
two_arg_name: two_arg_name.clone(),
three_arg_name: three_arg_name.clone(),
});
two_arg.create_air_vec(air_vec);
three_arg.create_air_vec(air_vec);
}
}
}
@ -1649,6 +1662,7 @@ impl AirTree {
| AirTree::FieldsEmpty { then, .. }
| AirTree::ListEmpty { then, .. }
| AirTree::NoOp { then } => then.return_type(),
AirTree::MultiValidator { .. } => void(),
}
}
@ -1725,7 +1739,8 @@ impl AirTree {
| AirTree::Fn { .. }
| AirTree::UnOp { .. }
| AirTree::WrapClause { .. }
| AirTree::Finally { .. } => vec![],
| AirTree::Finally { .. }
| AirTree::MultiValidator { .. } => vec![],
}
}
@ -2055,7 +2070,8 @@ impl AirTree {
| AirTree::Constr { .. }
| AirTree::RecordUpdate { .. }
| AirTree::ErrorTerm { .. }
| AirTree::Trace { .. } => {}
| AirTree::Trace { .. }
| AirTree::MultiValidator { .. } => {}
}
if !apply_with_func_last {
@ -2711,6 +2727,27 @@ impl AirTree {
apply_with_func_last,
);
}
AirTree::MultiValidator {
two_arg_name: _,
two_arg,
three_arg_name: _,
three_arg,
} => {
two_arg.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::SecondField,
with,
apply_with_func_last,
);
three_arg.do_traverse_tree_with(
tree_path,
current_depth + 1,
Fields::FourthField,
with,
apply_with_func_last,
)
}
}
if apply_with_func_last {
@ -3077,6 +3114,16 @@ impl AirTree {
| AirTree::ErrorTerm { .. } => {
panic!("A tree node with no children was encountered with a longer tree path.")
}
AirTree::MultiValidator {
two_arg_name: _,
two_arg,
three_arg_name: _,
three_arg,
} => match field {
Fields::SecondField => two_arg.as_mut().do_find_air_tree_node(tree_path_iter),
Fields::FourthField => three_arg.as_mut().do_find_air_tree_node(tree_path_iter),
_ => panic!("Tree Path index outside tree children nodes"),
},
}
} else {
self

View File

@ -14,6 +14,7 @@ pub fn aiken_optimize_and_intern(program: Program<Name>) -> Program<Name> {
.inline_reducer()
.force_delay_reducer()
.cast_data_reducer()
.builtin_eval_reducer()
.convert_arithmetic_ops()
.builtin_curry_reducer()
.lambda_reducer()

View File

@ -9,6 +9,7 @@ use crate::{
ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
builtins::DefaultFunction,
machine::cost_model::ExBudget,
};
use super::interner::CodeGenInterner;
@ -360,7 +361,7 @@ pub enum BuiltinArgs {
impl BuiltinArgs {
fn args_from_arg_stack(stack: Vec<(usize, Term<Name>)>, func: DefaultFunction) -> Self {
let error_safe = func.is_error_safe(&stack.iter().map(|(_, term)| term).collect_vec());
let error_safe = false;
let mut ordered_arg_stack = stack.into_iter().sorted_by(|(_, arg1), (_, arg2)| {
// sort by constant first if the builtin is order agnostic
@ -986,6 +987,27 @@ impl Term<Name> {
}
}
}
fn pierce_no_inlines<'a>(&'a self) -> &'a Self {
let mut term = self;
loop {
match term {
Term::Lambda {
parameter_name,
body,
} => {
if parameter_name.as_ref().text == NO_INLINE {
term = body;
} else {
break;
}
}
_ => break,
}
}
term
}
}
impl Program<Name> {
@ -1708,6 +1730,56 @@ impl Program<Name> {
step_b
}
pub fn builtin_eval_reducer(self) -> Self {
let mut applied_ids = vec![];
self.traverse_uplc_with(false, &mut |id, term, arg_stack, _scope| match term {
Term::Builtin(func) => {
let args = arg_stack
.iter()
.map(|(_, term)| term.pierce_no_inlines())
.collect_vec();
if func.can_curry_builtin()
&& arg_stack.len() == func.arity()
&& func.is_error_safe(&args)
{
let applied_term =
arg_stack
.into_iter()
.fold(Term::Builtin(*func), |acc, item| {
applied_ids.push(item.0);
acc.apply(item.1.pierce_no_inlines().clone())
});
// Check above for is error safe
let eval_term: Term<Name> = Program {
version: (1, 0, 0),
term: applied_term,
}
.to_named_debruijn()
.unwrap()
.eval(ExBudget::max())
.result()
.unwrap()
.try_into()
.unwrap();
*term = eval_term;
}
}
Term::Apply { function, .. } => {
let id = id.unwrap();
if applied_ids.contains(&id) {
*term = function.as_ref().clone();
}
}
Term::Constr { .. } => todo!(),
Term::Case { .. } => todo!(),
_ => {}
})
}
}
fn id_vec_function_to_var(func_name: &str, id_vec: &[usize]) -> String {

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1719353073, nanos_since_epoch = 642619000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1721828369, nanos_since_epoch = 230591000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]

View File

@ -13,4 +13,4 @@ requirements = []
source = "github"
[etags]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1719352929, nanos_since_epoch = 982815000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]
"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1721828402, nanos_since_epoch = 276492000 }, "a746f5b5cd3c2ca5dc19c43bcfc64230c546fafea2ba5f8e340c227b85886078"]

View File

@ -5,7 +5,7 @@
"plutusVersion": "v2",
"compiler": {
"name": "Aiken",
"version": "v1.0.29-alpha+e856fc6"
"version": "v1.0.29-alpha+06ac851"
}
},
"validators": [
@ -31,8 +31,8 @@
}
}
],
"compiledCode": "590347010000323232323232323232323223222323232322533300c323232533300f3007301137540022646464a66602c002020264a66602e603400426464a66602a601a602e6ea803854ccc054c8cc004004018894ccc06c004528099299980c19baf301e301b3754603c00402629444cc00c00c004c07800454ccc054c0300044cdc78010088a5015330164901536578706563740a202020202020202020206c6973742e616e7928696e707574732c20666e28696e70757429207b20696e7075742e6f75747075745f7265666572656e6365203d3d207574786f5f726566207d2900161533016491046275726e0016375a602e0046eb8c054004044c060004c94ccc048c024c050dd50008a5eb7bdb1804dd5980c180a9baa001323233001001323300100137566034603660366036603600a44a666032002297adef6c6013232323253330193372291100002153330193371e9101000021003100513301e337606ea4008dd3000998030030019bab301b003375c6032004603a004603600244a666030002298103d87a800013232323253330183372200e0042a66603066e3c01c0084cdd2a40006603a6e980052f5c02980103d87a8000133006006003375660340066eb8c060008c070008c068004dd7180b980a1baa0033758602c00260246ea800854cc041241236578706563742074782e4d696e7428706f6c6963795f696429203d20707572706f73650016301430150023013001300f37540022930a99806a491856616c696461746f722072657475726e65642066616c7365001365632533300b30030011533300f300e37540082930050a99980598010008a99980798071baa0041498028028c030dd50019b8748008dc3a4000a66666601e002200200c00c00c00c6eb800454cc00d24018f657870656374205b506169722861737365745f6e616d652c20616d6f756e74295d203d0a2020202020206d696e740a20202020202020207c3e2076616c75652e66726f6d5f6d696e7465645f76616c75650a20202020202020207c3e2076616c75652e746f6b656e7328706f6c6963795f6964290a20202020202020207c3e20646963742e746f5f70616972732829001615330024910c72646d723a20416374696f6e00165734ae7155ceaab9e5573eae815d0aba257481",
"hash": "e8c0b6d7c88bce7578f598ed61f172854b78a78c4ec251b45d6da4c4"
"compiledCode": "5901e4010000323232323232323223222323232322533300a323232533300d3007300e37540022646464a6660260022c264a666028602e00426464a666026601a60286ea803854ccc04cc8cc004004018894ccc060004528099299980b19baf301b30183754603600402629444cc00c00c004c06c00454ccc04cc0300044cdc78010088a501616375a60280046eb8c04800458c054004c94ccc040c024c044dd50008a5eb7bdb1804dd5980a98091baa00132323300100132330010013756602e603060306030603000a44a66602c002297adef6c60132323232533301733722910100002153330173371e9101000021003100513301b337606ea4008dd3000998030030019bab3018003375c602c0046034004603000244a66602a002298103d87a800013232323253330163372200e0042a66602c66e3c01c0084cdd2a4000660346e980052f5c02980103d87a80001330060060033756602e0066eb8c054008c064008c05c004dd7180a18089baa00337586026002601e6ea800858c044c048008c040004c030dd50008a4c26cac64a66601260060022a66601860166ea8010526161533300930020011533300c300b37540082930b0b18049baa003370e90011b87480014cccccc030004400458585858dd7000ab9a5573aaae7955cfaba05742ae895d201",
"hash": "41f948bc5dcda5f8813cc42ffde5c8d981f37be1475a5d242d44e2fd"
},
{
"title": "spend2.backtrace",
@ -48,8 +48,8 @@
"$ref": "#/definitions/Void"
}
},
"compiledCode": "5901680100003232323232323232323232232322322533300953330093005300b375464660020026eb0c040c044c044c034dd5180818069baa00222533300f00114c0103d87a800013232533300d4a2266e952000330120024bd70099802002000980980118088008a51153300a4914765787065637420536f6d65285f29203d206c6973742e66696e6428636f6e746578742e7472616e73616374696f6e2e6f7574707574732c20666e285f29207b2054727565207d290016149854cc0292411856616c696461746f722072657475726e65642066616c73650013656533333300f001153330073003300937540022a66601660146ea8004526005005005005005005533333300d002153330053001300737540042a66601260106ea8008526004004004004004004370e90000a99801a4810f5f72656465656d65723a20566f6964001615330024910c5f646174756d3a20566f696400165734ae7155ceaab9e5573eae815d0aba257481",
"hash": "f86c88144df93f3925cf048d1b50615fa95249f063a07242f70a6bd8"
"compiledCode": "58c401000032323232323232322323223225333007533300730053008375464660020026eb0c034c038c038c028dd5180698051baa00222533300c00114c0103d87a800013232533300b4a2266e9520003300f0024bd70099802002000980800118070008a511614984d9594cccccc03000454ccc014c00cc018dd50008a99980418039baa00114985858585858594cccccc02800854ccc00cc004c010dd50010a99980318029baa0021498585858585858dc3a4000ae6955ceaab9e5573eae815d0aba25749",
"hash": "419ffec4259b90352f8fde4cd451f98d01132f6cb7b1c382c9a3e810"
}
],
"definitions": {