charon_lib/transform/mod.rs
1pub mod check_generics;
2pub mod ctx;
3pub mod duplicate_defaulted_methods;
4pub mod duplicate_return;
5pub mod expand_associated_types;
6pub mod filter_invisible_trait_impls;
7pub mod filter_unreachable_blocks;
8pub mod graphs;
9pub mod hide_marker_traits;
10pub mod index_intermediate_assigns;
11pub mod index_to_function_calls;
12pub mod inline_local_panic_functions;
13pub mod inline_promoted_consts;
14pub mod insert_assign_return_unit;
15pub mod insert_storage_lives;
16pub mod lift_associated_item_clauses;
17pub mod merge_goto_chains;
18pub mod monomorphize;
19pub mod ops_to_function_calls;
20pub mod prettify_cfg;
21pub mod reconstruct_asserts;
22pub mod reconstruct_boxes;
23pub mod recover_body_comments;
24pub mod remove_drop_never;
25pub mod remove_dynamic_checks;
26pub mod remove_nops;
27pub mod remove_read_discriminant;
28pub mod remove_unit_locals;
29pub mod remove_unused_locals;
30pub mod remove_unused_methods;
31pub mod reorder_decls;
32pub mod simplify_constants;
33pub mod skip_trait_refs_when_known;
34pub mod ullbc_to_llbc;
35pub mod unbind_item_vars;
36pub mod update_block_indices;
37pub mod update_closure_signatures;
38
39pub use ctx::TransformCtx;
40use ctx::{LlbcPass, TransformPass, UllbcPass};
41use Pass::*;
42
43/// Item and type cleanup passes.
44pub static INITIAL_CLEANUP_PASSES: &[Pass] = &[
45 // Remove the trait/impl methods that were not translated (because not used).
46 NonBody(&remove_unused_methods::Transform),
47 // Move clauses on associated types to be parent clauses
48 NonBody(&lift_associated_item_clauses::Transform),
49 // Check that all supplied generic types match the corresponding generic parameters.
50 // Needs `lift_associated_item_clauses`.
51 NonBody(&check_generics::Check("after translation")),
52 // # Micro-pass: hide some overly-common traits we don't need: Sized, Sync, Allocator, etc..
53 NonBody(&hide_marker_traits::Transform),
54 // # Micro-pass: filter the trait impls that were marked invisible since we couldn't filter
55 // them out earlier.
56 NonBody(&filter_invisible_trait_impls::Transform),
57 // Add missing methods to trait impls by duplicating the default method.
58 NonBody(&duplicate_defaulted_methods::Transform),
59 // # Micro-pass: whenever we call a trait method on a known type, refer to the method `FunDecl`
60 // directly instead of going via a `TraitRef`. This is done before `reorder_decls` to remove
61 // some sources of mutual recursion.
62 UnstructuredBody(&skip_trait_refs_when_known::Transform),
63 // Change trait associated types to be type parameters instead. See the module for details.
64 NonBody(&expand_associated_types::Transform),
65];
66
67/// Body cleanup passes on the ullbc.
68pub static ULLBC_PASSES: &[Pass] = &[
69 // Inline promoted consts into their parent bodies.
70 UnstructuredBody(&inline_promoted_consts::Transform),
71 // # Micro-pass: merge single-origin gotos into their parent. This drastically reduces the
72 // graph size of the CFG.
73 UnstructuredBody(&merge_goto_chains::Transform),
74 // # Micro-pass: Remove overflow/div-by-zero/bounds checks since they are already part of the
75 // arithmetic/array operation in the semantics of (U)LLBC.
76 // **WARNING**: this pass uses the fact that the dynamic checks introduced by Rustc use a
77 // special "assert" construct. Because of this, it must happen *before* the
78 // [reconstruct_asserts] pass. See the comments in [crate::remove_dynamic_checks].
79 // **WARNING**: this pass relies on a precise structure of the MIR statements. Because of this,
80 // it must happen before passes that insert statements like [simplify_constants].
81 UnstructuredBody(&remove_dynamic_checks::Transform),
82 // # Micro-pass: reconstruct the special `Box::new` operations inserted e.g. in the `vec![]`
83 // macro.
84 // **WARNING**: this pass relies on a precise structure of the MIR statements. Because of this,
85 // it must happen before passes that insert statements like [simplify_constants].
86 // **WARNING**: this pass works across calls, hence must happen after `merge_goto_chains`,
87 UnstructuredBody(&reconstruct_boxes::Transform),
88 // # Micro-pass: desugar the constants to other values/operands as much
89 // as possible.
90 UnstructuredBody(&simplify_constants::Transform),
91 // # Micro-pass: the first local variable of closures is the
92 // closure itself. This is not consistent with the closure signature,
93 // which ignores this first variable. This micro-pass updates this.
94 UnstructuredBody(&update_closure_signatures::Transform),
95 // # Micro-pass: make sure the block ids used in the ULLBC are consecutive
96 UnstructuredBody(&update_block_indices::Transform),
97 // # Micro-pass: reconstruct the asserts
98 UnstructuredBody(&reconstruct_asserts::Transform),
99 // # Micro-pass: duplicate the return blocks
100 UnstructuredBody(&duplicate_return::Transform),
101 // # Micro-pass: filter the "dangling" blocks. Those might have been introduced by,
102 // for instance, [`reconstruct_asserts`].
103 UnstructuredBody(&filter_unreachable_blocks::Transform),
104 // # Micro-pass: `panic!()` expands to a new function definition each time. This pass cleans
105 // those up.
106 UnstructuredBody(&inline_local_panic_functions::Transform),
107 // # Micro-pass: introduce intermediate assignments in preparation of the
108 // [`index_to_function_calls`] pass.
109 UnstructuredBody(&index_intermediate_assigns::Transform),
110 // # Micro-pass: add the missing assignments to the return value.
111 // When the function return type is unit, the generated MIR doesn't
112 // set the return value to `()`. This can be a concern: in the case
113 // of Aeneas, it means the return variable contains ⊥ upon returning.
114 // For this reason, when the function has return type unit, we insert
115 // an extra assignment just before returning.
116 UnstructuredBody(&insert_assign_return_unit::Transform),
117 // # Micro-pass: remove locals of type `()` which show up a lot.
118 UnstructuredBody(&remove_unit_locals::Transform),
119 // # Micro-pass: remove the drops of locals whose type is `Never` (`!`). This
120 // is in preparation of the next transformation.
121 UnstructuredBody(&remove_drop_never::Transform),
122];
123
124/// Body cleanup passes after control flow reconstruction.
125pub static LLBC_PASSES: &[Pass] = &[
126 // # Go from ULLBC to LLBC (Low-Level Borrow Calculus) by reconstructing the control flow.
127 NonBody(&ullbc_to_llbc::Transform),
128 // # Micro-pass: replace some unops/binops and the array aggregates with
129 // function calls (introduces: ArrayToSlice, etc.)
130 StructuredBody(&ops_to_function_calls::Transform),
131 // # Micro-pass: replace the arrays/slices index operations with function
132 // calls.
133 // (introduces: ArrayIndexShared, ArrayIndexMut, etc.)
134 StructuredBody(&index_to_function_calls::Transform),
135 // # Micro-pass: Remove the discriminant reads (merge them with the switches)
136 StructuredBody(&remove_read_discriminant::Transform),
137 // Cleanup the cfg.
138 StructuredBody(&prettify_cfg::Transform),
139];
140
141/// Cleanup passes useful for both llbc and ullbc.
142pub static SHARED_FINALIZING_PASSES: &[Pass] = &[
143 // # Micro-pass: remove the locals which are never used.
144 NonBody(&remove_unused_locals::Transform),
145 // Insert storage lives for locals that are always allocated at the beginning of the function.
146 NonBody(&insert_storage_lives::Transform),
147 // # Micro-pass: remove the useless `StatementKind::Nop`s.
148 NonBody(&remove_nops::Transform),
149 // Monomorphize the functions and types.
150 NonBody(&monomorphize::Transform),
151 // # Micro-pass: take all the comments found in the original body and assign them to
152 // statements. This must be last after all the statement-affecting passes to avoid losing
153 // comments.
154 NonBody(&recover_body_comments::Transform),
155 // # Reorder the graph of dependencies and compute the strictly connex components to:
156 // - compute the order in which to extract the definitions
157 // - find the recursive definitions
158 // - group the mutually recursive definitions
159 NonBody(&reorder_decls::Transform),
160];
161
162/// Final passes to run at the end, after pretty-printing the llbc if applicable. These are only
163/// split from the above list to get test outputs even when generics fail to match.
164pub static FINAL_CLEANUP_PASSES: &[Pass] = &[
165 // Check that all supplied generic types match the corresponding generic parameters.
166 NonBody(&check_generics::Check("after transformations")),
167 // Use `DeBruijnVar::Free` for the variables bound in item signatures.
168 NonBody(&unbind_item_vars::Check),
169];
170
171#[derive(Clone, Copy)]
172pub enum Pass {
173 NonBody(&'static dyn TransformPass),
174 UnstructuredBody(&'static dyn UllbcPass),
175 StructuredBody(&'static dyn LlbcPass),
176}
177
178impl Pass {
179 pub fn run(self, ctx: &mut TransformCtx) {
180 match self {
181 NonBody(pass) => pass.transform_ctx(ctx),
182 UnstructuredBody(pass) => pass.transform_ctx(ctx),
183 StructuredBody(pass) => pass.transform_ctx(ctx),
184 }
185 }
186
187 pub fn name(&self) -> &str {
188 match self {
189 NonBody(pass) => pass.name(),
190 UnstructuredBody(pass) => pass.name(),
191 StructuredBody(pass) => pass.name(),
192 }
193 }
194}
195
196pub struct PrintCtxPass {
197 pub message: String,
198 /// Whether we're printing to stdout or only logging.
199 pub to_stdout: bool,
200}
201
202impl PrintCtxPass {
203 pub fn new(to_stdout: bool, message: String) -> &'static Self {
204 let ret = Self { message, to_stdout };
205 Box::leak(Box::new(ret))
206 }
207}
208
209impl TransformPass for PrintCtxPass {
210 fn transform_ctx(&self, ctx: &mut TransformCtx) {
211 let message = &self.message;
212 if self.to_stdout {
213 println!("{message}:\n\n{ctx}\n");
214 } else {
215 trace!("{message}:\n\n{ctx}\n");
216 }
217 }
218}