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