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