1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use std::borrow::Borrow;
use std::rc::Rc;
use clvm_rs::allocator::Allocator;
use crate::classic::clvm_tools::stages::stage_0::TRunProgram;
use crate::compiler::clvm::run;
use crate::compiler::codegen::get_callable;
use crate::compiler::comptypes::{BodyForm, Callable, CompilerOpts, PrimaryCodegen};
use crate::compiler::sexp::SExp;
use crate::compiler::srcloc::Srcloc;
use crate::util::u8_from_number;
fn is_at_form(head: Rc<BodyForm>) -> bool {
match head.borrow() {
BodyForm::Value(SExp::Atom(_, a)) => a.len() == 1 && a[0] == b'@',
_ => false,
}
}
pub fn optimize_expr(
allocator: &mut Allocator,
opts: Rc<dyn CompilerOpts>,
runner: Rc<dyn TRunProgram>,
compiler: &PrimaryCodegen,
body: Rc<BodyForm>,
) -> Option<(bool, Rc<BodyForm>)> {
match body.borrow() {
BodyForm::Quoted(_) => Some((true, body)),
BodyForm::Call(l, forms) => {
// () evaluates to ()
if forms.is_empty() {
return Some((true, body));
} else if is_at_form(forms[0].clone()) {
return None;
}
let mut examine_call = |al: Srcloc, an: &Vec<u8>| {
get_callable(
opts.clone(),
compiler,
l.clone(),
Rc::new(SExp::Atom(al, an.to_vec())),
)
.map(|calltype| match calltype {
// A macro invocation emits a bodyform, which we
// run back through the frontend and check.
Callable::CallMacro(_l, _) => None,
// A function is constant if its body is a constant
// expression or all its arguments are constant and
// its body doesn't include an environment reference.
Callable::CallDefun(_l, _target) => None,
// A primcall is constant if its arguments are constant
Callable::CallPrim(l, _) => {
let mut constant = true;
let optimized_args: Vec<(bool, Rc<BodyForm>)> = forms
.iter()
.skip(1)
.map(|a| {
let optimized = optimize_expr(
allocator,
opts.clone(),
runner.clone(),
compiler,
a.clone(),
);
constant = constant
&& optimized.as_ref().map(|x| x.0).unwrap_or_else(|| false);
optimized
.map(|x| (x.0, x.1))
.unwrap_or_else(|| (false, a.clone()))
})
.collect();
let mut result_list = vec![forms[0].clone()];
let mut replaced_args =
optimized_args.iter().map(|x| x.1.clone()).collect();
result_list.append(&mut replaced_args);
let code = BodyForm::Call(l.clone(), result_list);
if constant {
run(
allocator,
runner.clone(),
opts.prim_map(),
code.to_sexp(),
Rc::new(SExp::Nil(l)),
)
.map(|x| {
let x_borrow: &SExp = x.borrow();
Some((true, Rc::new(BodyForm::Quoted(x_borrow.clone()))))
})
.unwrap_or_else(|_| Some((false, Rc::new(code))))
} else {
Some((false, Rc::new(code)))
}
}
_ => None,
})
.unwrap_or_else(|_| None)
};
match forms[0].borrow() {
BodyForm::Value(SExp::Integer(al, an)) => {
examine_call(al.clone(), &u8_from_number(an.clone()))
}
BodyForm::Value(SExp::QuotedString(al, _, an)) => examine_call(al.clone(), an),
BodyForm::Value(SExp::Atom(al, an)) => examine_call(al.clone(), an),
_ => None,
}
}
BodyForm::Value(SExp::Integer(l, i)) => Some((
true,
Rc::new(BodyForm::Quoted(SExp::Integer(l.clone(), i.clone()))),
)),
_ => None,
}
}