]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/mod.rs
e03ef48f748388478efa6bf97257421a4bf3c0d5
[rust.git] / src / librustc_mir / transform / mod.rs
1 use crate::{shim, util};
2 use required_consts::RequiredConstsVisitor;
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_hir as hir;
5 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
6 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
7 use rustc_index::vec::IndexVec;
8 use rustc_middle::mir::visit::Visitor as _;
9 use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted};
10 use rustc_middle::ty::query::Providers;
11 use rustc_middle::ty::steal::Steal;
12 use rustc_middle::ty::{InstanceDef, TyCtxt, TypeFoldable};
13 use rustc_span::{Span, Symbol};
14 use std::borrow::Cow;
15
16 pub mod add_call_guards;
17 pub mod add_moves_for_packed_drops;
18 pub mod add_retag;
19 pub mod check_consts;
20 pub mod check_packed_ref;
21 pub mod check_unsafety;
22 pub mod cleanup_post_borrowck;
23 pub mod const_prop;
24 pub mod copy_prop;
25 pub mod deaggregator;
26 pub mod dump_mir;
27 pub mod elaborate_drops;
28 pub mod generator;
29 pub mod inline;
30 pub mod instcombine;
31 pub mod instrument_coverage;
32 pub mod no_landing_pads;
33 pub mod nrvo;
34 pub mod promote_consts;
35 pub mod qualify_min_const_fn;
36 pub mod remove_noop_landing_pads;
37 pub mod required_consts;
38 pub mod rustc_peek;
39 pub mod simplify;
40 pub mod simplify_branches;
41 pub mod simplify_try;
42 pub mod uninhabited_enum_branching;
43 pub mod unreachable_prop;
44 pub mod validate;
45
46 pub(crate) fn provide(providers: &mut Providers<'_>) {
47     self::check_unsafety::provide(providers);
48     *providers = Providers {
49         mir_keys,
50         mir_const,
51         mir_const_qualif,
52         mir_validated,
53         mir_drops_elaborated_and_const_checked,
54         optimized_mir,
55         is_mir_available,
56         promoted_mir,
57         ..*providers
58     };
59 }
60
61 fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
62     tcx.mir_keys(def_id.krate).contains(&def_id.expect_local())
63 }
64
65 /// Finds the full set of `DefId`s within the current crate that have
66 /// MIR associated with them.
67 fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> FxHashSet<LocalDefId> {
68     assert_eq!(krate, LOCAL_CRATE);
69
70     let mut set = FxHashSet::default();
71
72     // All body-owners have MIR associated with them.
73     set.extend(tcx.body_owners());
74
75     // Additionally, tuple struct/variant constructors have MIR, but
76     // they don't have a BodyId, so we need to build them separately.
77     struct GatherCtors<'a, 'tcx> {
78         tcx: TyCtxt<'tcx>,
79         set: &'a mut FxHashSet<LocalDefId>,
80     }
81     impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
82         fn visit_variant_data(
83             &mut self,
84             v: &'tcx hir::VariantData<'tcx>,
85             _: Symbol,
86             _: &'tcx hir::Generics<'tcx>,
87             _: hir::HirId,
88             _: Span,
89         ) {
90             if let hir::VariantData::Tuple(_, hir_id) = *v {
91                 self.set.insert(self.tcx.hir().local_def_id(hir_id));
92             }
93             intravisit::walk_struct_def(self, v)
94         }
95         type Map = intravisit::ErasedMap<'tcx>;
96         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
97             NestedVisitorMap::None
98         }
99     }
100     tcx.hir()
101         .krate()
102         .visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }.as_deep_visitor());
103
104     set
105 }
106
107 /// Where a specific `mir::Body` comes from.
108 #[derive(Debug, Copy, Clone)]
109 pub struct MirSource<'tcx> {
110     pub instance: InstanceDef<'tcx>,
111
112     /// If `Some`, this is a promoted rvalue within the parent function.
113     pub promoted: Option<Promoted>,
114 }
115
116 impl<'tcx> MirSource<'tcx> {
117     pub fn item(def_id: DefId) -> Self {
118         MirSource { instance: InstanceDef::Item(def_id), promoted: None }
119     }
120
121     #[inline]
122     pub fn def_id(&self) -> DefId {
123         self.instance.def_id()
124     }
125 }
126
127 /// Generates a default name for the pass based on the name of the
128 /// type `T`.
129 pub fn default_name<T: ?Sized>() -> Cow<'static, str> {
130     let name = ::std::any::type_name::<T>();
131     if let Some(tail) = name.rfind(':') { Cow::from(&name[tail + 1..]) } else { Cow::from(name) }
132 }
133
134 /// A streamlined trait that you can implement to create a pass; the
135 /// pass will be named after the type, and it will consist of a main
136 /// loop that goes over each available MIR and applies `run_pass`.
137 pub trait MirPass<'tcx> {
138     fn name(&self) -> Cow<'_, str> {
139         default_name::<Self>()
140     }
141
142     fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>);
143 }
144
145 pub fn run_passes(
146     tcx: TyCtxt<'tcx>,
147     body: &mut Body<'tcx>,
148     instance: InstanceDef<'tcx>,
149     promoted: Option<Promoted>,
150     mir_phase: MirPhase,
151     passes: &[&[&dyn MirPass<'tcx>]],
152 ) {
153     let phase_index = mir_phase.phase_index();
154     let source = MirSource { instance, promoted };
155     let validate = tcx.sess.opts.debugging_opts.validate_mir;
156
157     if body.phase >= mir_phase {
158         return;
159     }
160
161     if validate {
162         validate::Validator { when: format!("input to phase {:?}", mir_phase) }
163             .run_pass(tcx, source, body);
164     }
165
166     let mut index = 0;
167     let mut run_pass = |pass: &dyn MirPass<'tcx>| {
168         let run_hooks = |body: &_, index, is_after| {
169             dump_mir::on_mir_pass(
170                 tcx,
171                 &format_args!("{:03}-{:03}", phase_index, index),
172                 &pass.name(),
173                 source,
174                 body,
175                 is_after,
176             );
177         };
178         run_hooks(body, index, false);
179         pass.run_pass(tcx, source, body);
180         run_hooks(body, index, true);
181
182         if validate {
183             validate::Validator { when: format!("after {} in phase {:?}", pass.name(), mir_phase) }
184                 .run_pass(tcx, source, body);
185         }
186
187         index += 1;
188     };
189
190     for pass_group in passes {
191         for pass in *pass_group {
192             run_pass(*pass);
193         }
194     }
195
196     body.phase = mir_phase;
197
198     if mir_phase == MirPhase::Optimized {
199         validate::Validator { when: format!("end of phase {:?}", mir_phase) }
200             .run_pass(tcx, source, body);
201     }
202 }
203
204 fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
205     let const_kind = tcx.hir().body_const_context(def_id.expect_local());
206
207     // No need to const-check a non-const `fn`.
208     if const_kind.is_none() {
209         return Default::default();
210     }
211
212     // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
213     // cannot yet be stolen), because `mir_validated()`, which steals
214     // from `mir_const(), forces this query to execute before
215     // performing the steal.
216     let body = &tcx.mir_const(def_id).borrow();
217
218     if body.return_ty().references_error() {
219         tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
220         return Default::default();
221     }
222
223     let ccx =
224         check_consts::ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) };
225
226     let mut validator = check_consts::validation::Validator::new(&ccx);
227     validator.check_body();
228
229     // We return the qualifs in the return place for every MIR body, even though it is only used
230     // when deciding to promote a reference to a `const` for now.
231     validator.qualifs_in_return_place()
232 }
233
234 /// Make MIR ready for const evaluation. This is run on all MIR, not just on consts!
235 fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> Steal<Body<'_>> {
236     let def_id = def_id.expect_local();
237
238     // Unsafety check uses the raw mir, so make sure it is run.
239     let _ = tcx.unsafety_check_result(def_id);
240
241     let mut body = tcx.mir_built(def_id).steal();
242
243     util::dump_mir(tcx, None, "mir_map", &0, MirSource::item(def_id.to_def_id()), &body, |_, _| {
244         Ok(())
245     });
246
247     run_passes(
248         tcx,
249         &mut body,
250         InstanceDef::Item(def_id.to_def_id()),
251         None,
252         MirPhase::Const,
253         &[&[
254             // MIR-level lints.
255             &check_packed_ref::CheckPackedRef,
256             // What we need to do constant evaluation.
257             &simplify::SimplifyCfg::new("initial"),
258             &rustc_peek::SanityCheck,
259         ]],
260     );
261     tcx.alloc_steal_mir(body)
262 }
263
264 fn mir_validated(
265     tcx: TyCtxt<'tcx>,
266     def_id: LocalDefId,
267 ) -> (Steal<Body<'tcx>>, Steal<IndexVec<Promoted, Body<'tcx>>>) {
268     // Ensure that we compute the `mir_const_qualif` for constants at
269     // this point, before we steal the mir-const result.
270     let _ = tcx.mir_const_qualif(def_id.to_def_id());
271
272     let mut body = tcx.mir_const(def_id.to_def_id()).steal();
273
274     let mut required_consts = Vec::new();
275     let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
276     for (bb, bb_data) in traversal::reverse_postorder(&body) {
277         required_consts_visitor.visit_basic_block_data(bb, bb_data);
278     }
279     body.required_consts = required_consts;
280
281     let promote_pass = promote_consts::PromoteTemps::default();
282     run_passes(
283         tcx,
284         &mut body,
285         InstanceDef::Item(def_id.to_def_id()),
286         None,
287         MirPhase::Validated,
288         &[&[
289             // What we need to run borrowck etc.
290             &promote_pass,
291             // FIXME(richkadel): is this the best place for the InstrumentCoverage pass?
292             &instrument_coverage::InstrumentCoverage,
293             &simplify::SimplifyCfg::new("qualify-consts"),
294         ]],
295     );
296
297     let promoted = promote_pass.promoted_fragments.into_inner();
298     (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
299 }
300
301 fn mir_drops_elaborated_and_const_checked<'tcx>(
302     tcx: TyCtxt<'tcx>,
303     def_id: LocalDefId,
304 ) -> Steal<Body<'tcx>> {
305     // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
306     // execute before we can steal.
307     tcx.ensure().mir_borrowck(def_id);
308
309     let (body, _) = tcx.mir_validated(def_id);
310     let mut body = body.steal();
311
312     run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, None);
313     check_consts::post_drop_elaboration::check_live_drops(tcx, def_id, &body);
314     tcx.alloc_steal_mir(body)
315 }
316
317 /// After this series of passes, no lifetime analysis based on borrowing can be done.
318 fn run_post_borrowck_cleanup_passes<'tcx>(
319     tcx: TyCtxt<'tcx>,
320     body: &mut Body<'tcx>,
321     def_id: LocalDefId,
322     promoted: Option<Promoted>,
323 ) {
324     debug!("post_borrowck_cleanup({:?})", def_id);
325
326     let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
327         // Remove all things only needed by analysis
328         &no_landing_pads::NoLandingPads::new(tcx),
329         &simplify_branches::SimplifyBranches::new("initial"),
330         &remove_noop_landing_pads::RemoveNoopLandingPads,
331         &cleanup_post_borrowck::CleanupNonCodegenStatements,
332         &simplify::SimplifyCfg::new("early-opt"),
333         // These next passes must be executed together
334         &add_call_guards::CriticalCallEdges,
335         &elaborate_drops::ElaborateDrops,
336         &no_landing_pads::NoLandingPads::new(tcx),
337         // AddMovesForPackedDrops needs to run after drop
338         // elaboration.
339         &add_moves_for_packed_drops::AddMovesForPackedDrops,
340         // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
341         // but before optimizations begin.
342         &add_retag::AddRetag,
343         &simplify::SimplifyCfg::new("elaborate-drops"),
344     ];
345
346     run_passes(
347         tcx,
348         body,
349         InstanceDef::Item(def_id.to_def_id()),
350         promoted,
351         MirPhase::DropElab,
352         &[post_borrowck_cleanup],
353     );
354 }
355
356 fn run_optimization_passes<'tcx>(
357     tcx: TyCtxt<'tcx>,
358     body: &mut Body<'tcx>,
359     def_id: LocalDefId,
360     promoted: Option<Promoted>,
361 ) {
362     let optimizations: &[&dyn MirPass<'tcx>] = &[
363         &unreachable_prop::UnreachablePropagation,
364         &uninhabited_enum_branching::UninhabitedEnumBranching,
365         &simplify::SimplifyCfg::new("after-uninhabited-enum-branching"),
366         &inline::Inline,
367         // Lowering generator control-flow and variables has to happen before we do anything else
368         // to them. We do this inside the "optimizations" block so that it can benefit from
369         // optimizations that run before, that might be harder to do on the state machine than MIR
370         // with async primitives.
371         &generator::StateTransform,
372         &instcombine::InstCombine,
373         &const_prop::ConstProp,
374         &simplify_branches::SimplifyBranches::new("after-const-prop"),
375         // Run deaggregation here because:
376         //   1. Some codegen backends require it
377         //   2. It creates additional possibilities for some MIR optimizations to trigger
378         // FIXME(#70073): Why is this done here and not in `post_borrowck_cleanup`?
379         &deaggregator::Deaggregator,
380         &simplify_try::SimplifyArmIdentity,
381         &simplify_try::SimplifyBranchSame,
382         &copy_prop::CopyPropagation,
383         &simplify_branches::SimplifyBranches::new("after-copy-prop"),
384         &remove_noop_landing_pads::RemoveNoopLandingPads,
385         &simplify::SimplifyCfg::new("after-remove-noop-landing-pads"),
386         &simplify::SimplifyCfg::new("final"),
387         &nrvo::RenameReturnPlace,
388         &simplify::SimplifyLocals,
389     ];
390
391     let no_optimizations: &[&dyn MirPass<'tcx>] = &[
392         // Even if we don't do optimizations, we still have to lower generators for codegen.
393         &generator::StateTransform,
394         // FIXME(#70073): This pass is responsible for both optimization as well as some lints.
395         &const_prop::ConstProp,
396         // Even if we don't do optimizations, still run deaggregation because some backends assume
397         // that deaggregation always occurs.
398         &deaggregator::Deaggregator,
399     ];
400
401     let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[
402         &add_call_guards::CriticalCallEdges,
403         // Dump the end result for testing and debugging purposes.
404         &dump_mir::Marker("PreCodegen"),
405     ];
406
407     let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level;
408
409     #[rustfmt::skip]
410     run_passes(
411         tcx,
412         body,
413         InstanceDef::Item(def_id.to_def_id()),
414         promoted,
415         MirPhase::Optimized,
416         &[
417             if mir_opt_level > 0 { optimizations } else { no_optimizations },
418             pre_codegen_cleanup,
419         ],
420     );
421 }
422
423 fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
424     if tcx.is_constructor(def_id) {
425         // There's no reason to run all of the MIR passes on constructors when
426         // we can just output the MIR we want directly. This also saves const
427         // qualification and borrow checking the trouble of special casing
428         // constructors.
429         return shim::build_adt_ctor(tcx, def_id);
430     }
431
432     let def_id = def_id.expect_local();
433
434     let mut body = tcx.mir_drops_elaborated_and_const_checked(def_id).steal();
435     run_optimization_passes(tcx, &mut body, def_id, None);
436
437     debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR");
438
439     body
440 }
441
442 fn promoted_mir(tcx: TyCtxt<'_>, def_id: DefId) -> IndexVec<Promoted, Body<'_>> {
443     if tcx.is_constructor(def_id) {
444         return IndexVec::new();
445     }
446
447     let def_id = def_id.expect_local();
448
449     tcx.ensure().mir_borrowck(def_id);
450     let (_, promoted) = tcx.mir_validated(def_id);
451     let mut promoted = promoted.steal();
452
453     for (p, mut body) in promoted.iter_enumerated_mut() {
454         run_post_borrowck_cleanup_passes(tcx, &mut body, def_id, Some(p));
455         run_optimization_passes(tcx, &mut body, def_id, Some(p));
456     }
457
458     debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR");
459
460     promoted
461 }