Skip to content

Commit 179de6d

Browse files
authored
Merge pull request creusot-rs#804 from xldenis/basic-opts
2 parents f97dd54 + 2df46b1 commit 179de6d

File tree

350 files changed

+5387
-13150
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

350 files changed

+5387
-13150
lines changed

creusot/src/backend.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub(crate) mod constant;
1414
pub(crate) mod dependency;
1515
pub(crate) mod interface;
1616
pub(crate) mod logic;
17+
pub(crate) mod optimization;
1718
pub(crate) mod place;
1819
pub(crate) mod program;
1920
pub(crate) mod signature;
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
use std::collections::HashMap;
2+
3+
use rustc_middle::mir::{self};
4+
use rustc_span::Symbol;
5+
use std::collections::HashSet;
6+
7+
use crate::translation::{
8+
fmir,
9+
pearlite::{super_visit_term, Term, TermKind, TermVisitor},
10+
};
11+
12+
pub(crate) struct LocalUsage<'a, 'tcx> {
13+
locals: &'a fmir::LocalDecls<'tcx>,
14+
pub(crate) usages: HashMap<Symbol, Usage>,
15+
}
16+
17+
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
18+
pub(crate) enum ZeroOneMany {
19+
#[default]
20+
Zero,
21+
One(Whole),
22+
Many,
23+
}
24+
25+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26+
pub(crate) enum Whole {
27+
Whole,
28+
Part,
29+
}
30+
31+
impl ZeroOneMany {
32+
fn inc(&mut self, whole: Whole) {
33+
*self = match self {
34+
ZeroOneMany::Zero => ZeroOneMany::One(whole),
35+
ZeroOneMany::One(_) => ZeroOneMany::Many,
36+
ZeroOneMany::Many => ZeroOneMany::Many,
37+
}
38+
}
39+
}
40+
41+
#[derive(Clone, Copy, Default, Debug)]
42+
pub(crate) struct Usage {
43+
write: ZeroOneMany,
44+
read: ZeroOneMany,
45+
temp_var: bool,
46+
// Is this local used in a place where we need a `Term`?
47+
used_in_pure_ctx: bool,
48+
}
49+
50+
pub(crate) fn gather_usage(b: &fmir::Body) -> HashMap<Symbol, Usage> {
51+
let mut usage = LocalUsage { locals: &b.locals, usages: HashMap::new() };
52+
53+
usage.visit_body(b);
54+
usage.usages
55+
}
56+
57+
impl<'a, 'tcx> LocalUsage<'a, 'tcx> {
58+
pub(crate) fn visit_body(&mut self, b: &fmir::Body<'tcx>) {
59+
b.blocks.values().for_each(|b| self.visit_block(b));
60+
}
61+
62+
fn visit_block(&mut self, b: &fmir::Block<'tcx>) {
63+
b.stmts.iter().for_each(|s| self.visit_statement(s));
64+
self.visit_terminator(&b.terminator);
65+
}
66+
67+
fn visit_terminator(&mut self, t: &fmir::Terminator<'tcx>) {
68+
match t {
69+
fmir::Terminator::Switch(e, _) => self.visit_expr(e),
70+
fmir::Terminator::Return => {
71+
self.read(Symbol::intern("_0"), true);
72+
self.read(Symbol::intern("_0"), true);
73+
}
74+
_ => {}
75+
}
76+
}
77+
78+
fn visit_statement(&mut self, b: &fmir::Statement<'tcx>) {
79+
match b {
80+
fmir::Statement::Assignment(p, r) => {
81+
self.write_place(p);
82+
self.visit_rvalue(r)
83+
}
84+
fmir::Statement::Resolve(_, _, p) => {
85+
self.read_place(p);
86+
self.read_place(p)
87+
}
88+
fmir::Statement::Assertion { cond, msg: _ } => {
89+
// Make assertions stop propagation because it would require Expr -> Term translation
90+
self.visit_term(cond);
91+
self.visit_term(cond);
92+
}
93+
fmir::Statement::Invariant(t) => self.visit_term(t),
94+
fmir::Statement::Variant(t) => self.visit_term(t),
95+
}
96+
}
97+
98+
fn visit_rvalue(&mut self, r: &fmir::RValue<'tcx>) {
99+
match r {
100+
fmir::RValue::Ghost(t) => self.visit_term(t),
101+
fmir::RValue::Borrow(p) => {
102+
self.read_place(p);
103+
self.read_place(p)
104+
}
105+
fmir::RValue::Expr(e) => self.visit_expr(e),
106+
}
107+
}
108+
109+
// fn visit_term(&mut self, t: &Term<'tcx>) {}
110+
111+
fn visit_expr(&mut self, e: &fmir::Expr<'tcx>) {
112+
match e {
113+
fmir::Expr::Move(p) => self.read_place(p),
114+
fmir::Expr::Copy(p) => self.read_place(p),
115+
fmir::Expr::BinOp(_, _, l, r) => {
116+
self.visit_expr(l);
117+
self.visit_expr(r)
118+
}
119+
fmir::Expr::UnaryOp(_, _, e) => self.visit_expr(e),
120+
fmir::Expr::Constructor(_, _, es) => es.iter().for_each(|e| self.visit_expr(e)),
121+
fmir::Expr::Call(_, _, es) => es.iter().for_each(|e| self.visit_expr(e)),
122+
fmir::Expr::Constant(t) => self.visit_term(t),
123+
fmir::Expr::Cast(e, _, _) => self.visit_expr(e),
124+
fmir::Expr::Tuple(es) => es.iter().for_each(|e| self.visit_expr(e)),
125+
fmir::Expr::Span(_, e) => self.visit_expr(e),
126+
fmir::Expr::Len(e) => self.visit_expr(e),
127+
fmir::Expr::Array(es) => es.iter().for_each(|e| self.visit_expr(e)),
128+
fmir::Expr::Repeat(l, r) => {
129+
self.visit_expr(l);
130+
self.visit_expr(r)
131+
}
132+
}
133+
}
134+
135+
fn read_place(&mut self, p: &fmir::Place<'tcx>) {
136+
self.read(p.local, p.projection.is_empty());
137+
p.projection.iter().for_each(|p| match p {
138+
mir::ProjectionElem::Index(l) => self.read(*l, true),
139+
_ => {}
140+
})
141+
}
142+
143+
fn write_place(&mut self, p: &fmir::Place<'tcx>) {
144+
self.write(p.local, p.projection.is_empty());
145+
p.projection.iter().for_each(|p| match p {
146+
mir::ProjectionElem::Index(l) => self.read(*l, true),
147+
_ => {}
148+
})
149+
}
150+
151+
fn read(&mut self, local: Symbol, whole: bool) {
152+
if let Some(usage) = self.get(local) {
153+
usage.read.inc(if whole { Whole::Whole } else { Whole::Part })
154+
};
155+
}
156+
157+
fn get(&mut self, local: Symbol) -> Option<&mut Usage> {
158+
if !self.locals.contains_key(&local) {
159+
return None;
160+
}
161+
162+
Some(
163+
self.usages.entry(local).or_insert_with(|| Usage {
164+
temp_var: self.locals[&local].temp,
165+
..Default::default()
166+
}),
167+
)
168+
}
169+
fn write(&mut self, local: Symbol, whole: bool) {
170+
if let Some(usage) = self.get(local) {
171+
usage.write.inc(if whole { Whole::Whole } else { Whole::Part })
172+
};
173+
}
174+
175+
fn pure(&mut self, local: Symbol) {
176+
if let Some(usage) = self.get(local) {
177+
usage.used_in_pure_ctx = true
178+
};
179+
}
180+
}
181+
182+
impl<'a, 'tcx> TermVisitor<'tcx> for LocalUsage<'a, 'tcx> {
183+
fn visit_term(&mut self, term: &Term<'tcx>) {
184+
match term.kind {
185+
TermKind::Var(v) => {
186+
self.pure(v);
187+
self.read(v, true);
188+
}
189+
_ => super_visit_term(term, self),
190+
}
191+
}
192+
}
193+
194+
struct SimplePropagator<'tcx> {
195+
usage: HashMap<Symbol, Usage>,
196+
prop: HashMap<Symbol, fmir::Expr<'tcx>>,
197+
dead: HashSet<Symbol>,
198+
}
199+
200+
pub(crate) fn simplify_fmir<'tcx>(usage: HashMap<Symbol, Usage>, body: &mut fmir::Body) {
201+
SimplePropagator { usage, prop: HashMap::new(), dead: HashSet::new() }.visit_body(body);
202+
}
203+
impl<'tcx> SimplePropagator<'tcx> {
204+
fn visit_body(&mut self, b: &mut fmir::Body<'tcx>) {
205+
for b in b.blocks.values_mut() {
206+
self.visit_block(b)
207+
}
208+
209+
b.locals.retain(|l, _| !self.dead.contains(&l) && self.usage.contains_key(&l));
210+
211+
assert!(self.prop.is_empty(), "some values were not properly propagated {:?}", self.prop)
212+
}
213+
214+
fn visit_block(&mut self, b: &mut fmir::Block<'tcx>) {
215+
let mut out_stmts = Vec::with_capacity(b.stmts.len());
216+
217+
for mut s in std::mem::take(&mut b.stmts) {
218+
self.visit_statement(&mut s);
219+
match s {
220+
fmir::Statement::Assignment(l, fmir::RValue::Expr(r))
221+
if self.should_propagate(l.local) && !self.usage[&l.local].used_in_pure_ctx => {
222+
self.prop.insert(l.local, r);
223+
self.dead.insert(l.local);
224+
}
225+
fmir::Statement::Assignment(ref l, fmir::RValue::Expr(ref r)) if self.should_erase(l.local) && !r.is_call() && r.is_pure() => {
226+
self.dead.insert(l.local);
227+
}
228+
fmir::Statement::Resolve(_,_, ref p) => {
229+
if let Some(l) = p.as_symbol() && self.dead.contains(&l) {
230+
} else {
231+
out_stmts.push(s)
232+
}
233+
}
234+
_ => out_stmts.push(s),
235+
}
236+
}
237+
b.stmts = out_stmts;
238+
239+
match &mut b.terminator {
240+
fmir::Terminator::Goto(_) => {}
241+
fmir::Terminator::Switch(e, _) => self.visit_expr(e),
242+
fmir::Terminator::Return => {}
243+
fmir::Terminator::Abort => {}
244+
}
245+
}
246+
247+
fn visit_statement(&mut self, s: &mut fmir::Statement<'tcx>) {
248+
match s {
249+
fmir::Statement::Assignment(_, r) => self.visit_rvalue(r),
250+
fmir::Statement::Resolve(_, _, p) => {
251+
if let Some(l) = p.as_symbol() && self.dead.contains(&l) {
252+
253+
}
254+
}
255+
fmir::Statement::Assertion { cond, msg: _ } => self.visit_term(cond),
256+
fmir::Statement::Invariant(t) => self.visit_term(t),
257+
fmir::Statement::Variant(t) => self.visit_term(t),
258+
}
259+
}
260+
261+
fn visit_rvalue(&mut self, r: &mut fmir::RValue<'tcx>) {
262+
match r {
263+
fmir::RValue::Ghost(t) => self.visit_term(t),
264+
fmir::RValue::Borrow(p) => {
265+
assert!(self.prop.get(&p.local).is_none(), "Trying to propagate borrowed variable")
266+
}
267+
fmir::RValue::Expr(e) => self.visit_expr(e),
268+
}
269+
}
270+
271+
fn visit_expr(&mut self, e: &mut fmir::Expr<'tcx>) {
272+
match e {
273+
fmir::Expr::Move(p) | fmir::Expr::Copy(p) => {
274+
if let Some(l) = p.as_symbol() && let Some(v) = self.prop.remove(&l) {
275+
*e = v;
276+
}
277+
},
278+
fmir::Expr::BinOp(_, _, l, r) => {
279+
self.visit_expr(l);
280+
self.visit_expr(r)
281+
}
282+
fmir::Expr::UnaryOp(_, _, e) => self.visit_expr(e),
283+
fmir::Expr::Constructor(_, _, es) => es.iter_mut().for_each(|e| self.visit_expr(e)),
284+
fmir::Expr::Call(_, _, es) => es.iter_mut().for_each(|e| self.visit_expr(e)),
285+
fmir::Expr::Constant(t) => self.visit_term(t),
286+
fmir::Expr::Cast(e, _, _) => self.visit_expr(e),
287+
fmir::Expr::Tuple(es) => es.iter_mut().for_each(|e| self.visit_expr(e)),
288+
fmir::Expr::Span(_, e) => self.visit_expr(e),
289+
fmir::Expr::Len(e) => self.visit_expr(e),
290+
fmir::Expr::Array(es) => es.iter_mut().for_each(|e| self.visit_expr(e)),
291+
fmir::Expr::Repeat(l, r) => {
292+
self.visit_expr(l);
293+
self.visit_expr(r)
294+
}
295+
}
296+
}
297+
298+
fn visit_term(&mut self, _t: &mut Term<'tcx>) {
299+
// TODO: Find a way to propagate Expr into Term
300+
// _t.subst_with(|s| {
301+
// let x = self.usage.iter().find(|(k, _)| LocalIdent::anon(k).symbol() == s );
302+
303+
// x.and_then(|l| self.prop.remove(l))
304+
// })
305+
}
306+
307+
fn should_propagate(&self, l: Symbol) -> bool {
308+
self.usage
309+
.get(&l)
310+
.map(|u| {
311+
u.read == ZeroOneMany::One(Whole::Whole)
312+
&& u.write == ZeroOneMany::One(Whole::Whole)
313+
&& u.temp_var
314+
})
315+
.unwrap_or(false)
316+
}
317+
318+
fn should_erase(&self, l: Symbol) -> bool {
319+
self.usage
320+
.get(&l)
321+
.map(|u| {
322+
u.read == ZeroOneMany::Zero && matches!(u.write, ZeroOneMany::One(_)) && u.temp_var
323+
})
324+
.unwrap_or(false)
325+
}
326+
}

0 commit comments

Comments
 (0)