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