]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | //! Removes assignments to ZST places. |
2 | ||
c295e0f8 | 3 | use crate::MirPass; |
cdc7bbd5 XL |
4 | use rustc_middle::mir::tcx::PlaceTy; |
5 | use rustc_middle::mir::{Body, LocalDecls, Place, StatementKind}; | |
6 | use rustc_middle::ty::{self, Ty, TyCtxt}; | |
7 | ||
8 | pub struct RemoveZsts; | |
9 | ||
10 | impl<'tcx> MirPass<'tcx> for RemoveZsts { | |
a2a8927a XL |
11 | fn is_enabled(&self, sess: &rustc_session::Session) -> bool { |
12 | sess.mir_opt_level() > 0 | |
13 | } | |
14 | ||
cdc7bbd5 | 15 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
94222f64 XL |
16 | // Avoid query cycles (generators require optimized MIR for layout). |
17 | if tcx.type_of(body.source.def_id()).is_generator() { | |
cdc7bbd5 XL |
18 | return; |
19 | } | |
20 | let param_env = tcx.param_env(body.source.def_id()); | |
21 | let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); | |
22 | for block in basic_blocks.iter_mut() { | |
23 | for statement in block.statements.iter_mut() { | |
17df50a5 XL |
24 | if let StatementKind::Assign(box (place, _)) = statement.kind { |
25 | let place_ty = place.ty(local_decls, tcx).ty; | |
26 | if !maybe_zst(place_ty) { | |
27 | continue; | |
28 | } | |
5e7ed085 FG |
29 | let Ok(layout) = tcx.layout_of(param_env.and(place_ty)) else { |
30 | continue; | |
17df50a5 XL |
31 | }; |
32 | if !layout.is_zst() { | |
33 | continue; | |
34 | } | |
35 | if involves_a_union(place, local_decls, tcx) { | |
36 | continue; | |
37 | } | |
38 | if tcx.consider_optimizing(|| { | |
39 | format!( | |
40 | "RemoveZsts - Place: {:?} SourceInfo: {:?}", | |
41 | place, statement.source_info | |
42 | ) | |
43 | }) { | |
44 | statement.make_nop(); | |
cdc7bbd5 | 45 | } |
cdc7bbd5 XL |
46 | } |
47 | } | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | /// A cheap, approximate check to avoid unnecessary `layout_of` calls. | |
53 | fn maybe_zst(ty: Ty<'_>) -> bool { | |
54 | match ty.kind() { | |
55 | // maybe ZST (could be more precise) | |
56 | ty::Adt(..) | ty::Array(..) | ty::Closure(..) | ty::Tuple(..) | ty::Opaque(..) => true, | |
57 | // definitely ZST | |
58 | ty::FnDef(..) | ty::Never => true, | |
59 | // unreachable or can't be ZST | |
60 | _ => false, | |
61 | } | |
62 | } | |
63 | ||
64 | /// Miri lazily allocates memory for locals on assignment, | |
65 | /// so we must preserve writes to unions and union fields, | |
66 | /// or it will ICE on reads of those fields. | |
67 | fn involves_a_union<'tcx>( | |
68 | place: Place<'tcx>, | |
69 | local_decls: &LocalDecls<'tcx>, | |
70 | tcx: TyCtxt<'tcx>, | |
71 | ) -> bool { | |
72 | let mut place_ty = PlaceTy::from_ty(local_decls[place.local].ty); | |
17df50a5 | 73 | if place_ty.ty.is_union() { |
cdc7bbd5 XL |
74 | return true; |
75 | } | |
76 | for elem in place.projection { | |
77 | place_ty = place_ty.projection_ty(tcx, elem); | |
17df50a5 | 78 | if place_ty.ty.is_union() { |
cdc7bbd5 XL |
79 | return true; |
80 | } | |
81 | } | |
82 | return false; | |
83 | } |