1- //! Removes assignments to ZST places.
1+ //! Removes operations on ZST places, and convert ZST operands to constants .
22
33use crate :: MirPass ;
4- use rustc_middle:: mir:: { Body , StatementKind } ;
4+ use rustc_middle:: mir:: interpret:: ConstValue ;
5+ use rustc_middle:: mir:: visit:: * ;
6+ use rustc_middle:: mir:: * ;
57use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
68
79pub struct RemoveZsts ;
@@ -16,38 +18,24 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
1618 if tcx. type_of ( body. source . def_id ( ) ) . subst_identity ( ) . is_generator ( ) {
1719 return ;
1820 }
19- let param_env = tcx. param_env ( body. source . def_id ( ) ) ;
20- let basic_blocks = body. basic_blocks . as_mut_preserves_cfg ( ) ;
21+ let param_env = tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) ;
2122 let local_decls = & body. local_decls ;
22- for block in basic_blocks {
23- for statement in block. statements . iter_mut ( ) {
24- if let StatementKind :: Assign ( box ( place, _) ) | StatementKind :: Deinit ( box place) =
25- statement. kind
26- {
27- let place_ty = place. ty ( local_decls, tcx) . ty ;
28- if !maybe_zst ( place_ty) {
29- continue ;
30- }
31- let Ok ( layout) = tcx. layout_of ( param_env. and ( place_ty) ) else {
32- continue ;
33- } ;
34- if !layout. is_zst ( ) {
35- continue ;
36- }
37- if tcx. consider_optimizing ( || {
38- format ! (
39- "RemoveZsts - Place: {:?} SourceInfo: {:?}" ,
40- place, statement. source_info
41- )
42- } ) {
43- statement. make_nop ( ) ;
44- }
45- }
46- }
23+ let mut replacer = Replacer { tcx, param_env, local_decls } ;
24+ for var_debug_info in & mut body. var_debug_info {
25+ replacer. visit_var_debug_info ( var_debug_info) ;
26+ }
27+ for ( bb, data) in body. basic_blocks . as_mut_preserves_cfg ( ) . iter_enumerated_mut ( ) {
28+ replacer. visit_basic_block_data ( bb, data) ;
4729 }
4830 }
4931}
5032
33+ struct Replacer < ' a , ' tcx > {
34+ tcx : TyCtxt < ' tcx > ,
35+ param_env : ty:: ParamEnv < ' tcx > ,
36+ local_decls : & ' a LocalDecls < ' tcx > ,
37+ }
38+
5139/// A cheap, approximate check to avoid unnecessary `layout_of` calls.
5240fn maybe_zst ( ty : Ty < ' _ > ) -> bool {
5341 match ty. kind ( ) {
@@ -63,3 +51,93 @@ fn maybe_zst(ty: Ty<'_>) -> bool {
6351 _ => false ,
6452 }
6553}
54+
55+ impl < ' tcx > Replacer < ' _ , ' tcx > {
56+ fn known_to_be_zst ( & self , ty : Ty < ' tcx > ) -> bool {
57+ if !maybe_zst ( ty) {
58+ return false ;
59+ }
60+ let Ok ( layout) = self . tcx . layout_of ( self . param_env . and ( ty) ) else {
61+ return false ;
62+ } ;
63+ layout. is_zst ( )
64+ }
65+
66+ fn make_zst ( & self , ty : Ty < ' tcx > ) -> Constant < ' tcx > {
67+ debug_assert ! ( self . known_to_be_zst( ty) ) ;
68+ Constant {
69+ span : rustc_span:: DUMMY_SP ,
70+ user_ty : None ,
71+ literal : ConstantKind :: Val ( ConstValue :: ZeroSized , ty) ,
72+ }
73+ }
74+ }
75+
76+ impl < ' tcx > MutVisitor < ' tcx > for Replacer < ' _ , ' tcx > {
77+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
78+ self . tcx
79+ }
80+
81+ fn visit_var_debug_info ( & mut self , var_debug_info : & mut VarDebugInfo < ' tcx > ) {
82+ match var_debug_info. value {
83+ VarDebugInfoContents :: Const ( _) => { }
84+ VarDebugInfoContents :: Place ( place) => {
85+ let place_ty = place. ty ( self . local_decls , self . tcx ) . ty ;
86+ if self . known_to_be_zst ( place_ty) {
87+ var_debug_info. value = VarDebugInfoContents :: Const ( self . make_zst ( place_ty) )
88+ }
89+ }
90+ VarDebugInfoContents :: Composite { ty, fragments : _ } => {
91+ if self . known_to_be_zst ( ty) {
92+ var_debug_info. value = VarDebugInfoContents :: Const ( self . make_zst ( ty) )
93+ }
94+ }
95+ }
96+ }
97+
98+ fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , loc : Location ) {
99+ if let Operand :: Constant ( _) = operand {
100+ return ;
101+ }
102+ let op_ty = operand. ty ( self . local_decls , self . tcx ) ;
103+ if self . known_to_be_zst ( op_ty)
104+ && self . tcx . consider_optimizing ( || {
105+ format ! ( "RemoveZsts - Operand: {:?} Location: {:?}" , operand, loc)
106+ } )
107+ {
108+ * operand = Operand :: Constant ( Box :: new ( self . make_zst ( op_ty) ) )
109+ }
110+ }
111+
112+ fn visit_statement ( & mut self , statement : & mut Statement < ' tcx > , loc : Location ) {
113+ let place_for_ty = match statement. kind {
114+ StatementKind :: Assign ( box ( place, ref rvalue) ) => {
115+ rvalue. is_safe_to_remove ( ) . then_some ( place)
116+ }
117+ StatementKind :: Deinit ( box place)
118+ | StatementKind :: SetDiscriminant { box place, variant_index : _ }
119+ | StatementKind :: AscribeUserType ( box ( place, _) , _)
120+ | StatementKind :: Retag ( _, box place)
121+ | StatementKind :: PlaceMention ( box place)
122+ | StatementKind :: FakeRead ( box ( _, place) ) => Some ( place) ,
123+ StatementKind :: StorageLive ( local) | StatementKind :: StorageDead ( local) => {
124+ Some ( local. into ( ) )
125+ }
126+ StatementKind :: Coverage ( _)
127+ | StatementKind :: Intrinsic ( _)
128+ | StatementKind :: Nop
129+ | StatementKind :: ConstEvalCounter => None ,
130+ } ;
131+ if let Some ( place_for_ty) = place_for_ty
132+ && let ty = place_for_ty. ty ( self . local_decls , self . tcx ) . ty
133+ && self . known_to_be_zst ( ty)
134+ && self . tcx . consider_optimizing ( || {
135+ format ! ( "RemoveZsts - Place: {:?} SourceInfo: {:?}" , place_for_ty, statement. source_info)
136+ } )
137+ {
138+ statement. make_nop ( ) ;
139+ } else {
140+ self . super_statement ( statement, loc) ;
141+ }
142+ }
143+ }
0 commit comments