50dff955a9
Implement SSA-based reference propagation Rust has a tendency to create a lot of short-lived borrows, in particular for method calls. This PR aims to remove those short-lived borrows with a const-propagation dedicated to pointers to local places. This pass aims to transform the following pattern: ``` _1 = &raw? mut? PLACE; _3 = *_1; _4 = &raw? mut? *_1; ``` Into ``` _1 = &raw? mut? PLACE; _3 = PLACE; _4 = &raw? mut? PLACE; ``` where `PLACE` is a direct or an indirect place expression. By removing indirection, this pass should help both dest-prop and const-prop to handle more cases. This optimization is distinct from const-prop and dataflow const-prop since the borrow-reborrow patterns needs to preserve borrowck invariants, especially the uniqueness property of mutable references. The pointed-to places are computed using a SSA analysis. We suppose that removable borrows are typically temporaries from autoref, so they are by construction assigned only once, and a SSA analysis is enough to catch them. For each local, we store both where and how it is used, in order to efficiently compute the all-or-nothing property. Thanks to `Derefer`, we only have to track locals, not places in general. --- There are 3 properties that need to be upheld for this transformation to be legal: - place constness: `PLACE` must refer to the same memory wherever it appears; - pointer liveness: we must not introduce dereferences of dangling pointers; - `&mut` borrow uniqueness. ## Constness If `PLACE` is an indirect projection, if its of the form `(*LOCAL).PROJECTIONS` where: - `LOCAL` is SSA; - all projections in `PROJECTIONS` are constant (no dereference and no indexing). If `PLACE` is a direct projection of a local, we consider it as constant if: - the local is always live, or it has a single `StorageLive` that dominates all uses; - all projections are constant. # Liveness When performing a substitution, we must take care not to introduce uses of dangling locals. Using a dangling borrow is UB. Therefore, we assume that for any use of `*x`, where `x` is a borrow, the pointed-to memory is live. Limitations: - occurrences of `*x` in an `&raw mut? *x` are accepted; - raw pointers are allowed to be dangling. In those 2 case, we do not substitute anything, to be on the safe side. **Open question:** we do not differentiate borrows of ZST and non-ZST. The UB rules may be different depending on the layout. Having a different treatment would effectively prevent this pass from running on polymorphic MIR, which defeats the purpose of MIR opts. ## Uniqueness For `&mut` borrows, we also need to preserve the uniqueness property: we must avoid creating a state where we interleave uses of `*_1` and `_2`. To do it, we only perform full substitution of mutable borrows: we replace either all or none of the occurrences of `*_1`. Some care has to be taken when `_1` is copied in other locals. ``` _1 = &raw? mut? _2; _3 = *_1; _4 = _1 _5 = *_4 ``` In such cases, fully substituting `_1` means fully substituting all of the copies. For immutable borrows, we do not need to preserve such uniqueness property, so we perform all the possible substitutions without removing the `_1 = &_2` statement. |
||
---|---|---|
.. | ||
rustc | ||
rustc_abi | ||
rustc_apfloat | ||
rustc_arena | ||
rustc_ast | ||
rustc_ast_lowering | ||
rustc_ast_passes | ||
rustc_ast_pretty | ||
rustc_attr | ||
rustc_baked_icu_data | ||
rustc_borrowck | ||
rustc_builtin_macros | ||
rustc_codegen_cranelift | ||
rustc_codegen_gcc | ||
rustc_codegen_llvm | ||
rustc_codegen_ssa | ||
rustc_const_eval | ||
rustc_data_structures | ||
rustc_driver | ||
rustc_driver_impl | ||
rustc_error_codes | ||
rustc_error_messages | ||
rustc_errors | ||
rustc_expand | ||
rustc_feature | ||
rustc_fluent_macro | ||
rustc_fs_util | ||
rustc_graphviz | ||
rustc_hir | ||
rustc_hir_analysis | ||
rustc_hir_pretty | ||
rustc_hir_typeck | ||
rustc_incremental | ||
rustc_index | ||
rustc_infer | ||
rustc_interface | ||
rustc_lexer | ||
rustc_lint | ||
rustc_lint_defs | ||
rustc_llvm | ||
rustc_log | ||
rustc_macros | ||
rustc_metadata | ||
rustc_middle | ||
rustc_mir_build | ||
rustc_mir_dataflow | ||
rustc_mir_transform | ||
rustc_monomorphize | ||
rustc_parse | ||
rustc_parse_format | ||
rustc_passes | ||
rustc_plugin_impl | ||
rustc_privacy | ||
rustc_query_impl | ||
rustc_query_system | ||
rustc_resolve | ||
rustc_serialize | ||
rustc_session | ||
rustc_smir | ||
rustc_span | ||
rustc_symbol_mangling | ||
rustc_target | ||
rustc_trait_selection | ||
rustc_traits | ||
rustc_transmute | ||
rustc_ty_utils | ||
rustc_type_ir |