charon_lib/transform/
mod.rs

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