1
+ use rustc_data_structures:: fx:: FxIndexMap ;
1
2
use rustc_index:: bit_set:: BitSet ;
2
3
use rustc_index:: IndexSlice ;
3
4
use rustc_middle:: mir:: visit:: * ;
@@ -37,6 +38,8 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
37
38
38
39
let fully_moved = fully_moved_locals ( & ssa, body) ;
39
40
debug ! ( ?fully_moved) ;
41
+ let const_locals = const_locals ( & ssa, body) ;
42
+ debug ! ( ?const_locals) ;
40
43
41
44
let mut storage_to_remove = BitSet :: new_empty ( fully_moved. domain_size ( ) ) ;
42
45
for ( local, & head) in ssa. copy_classes ( ) . iter_enumerated ( ) {
@@ -45,12 +48,13 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
45
48
}
46
49
}
47
50
48
- let any_replacement = ssa . copy_classes ( ) . iter_enumerated ( ) . any ( | ( l , & h ) | l != h ) ;
51
+ let any_replacement = !storage_to_remove . is_empty ( ) || !const_locals . is_empty ( ) ;
49
52
50
53
Replacer {
51
54
tcx,
52
55
copy_classes : ssa. copy_classes ( ) ,
53
56
fully_moved,
57
+ const_locals,
54
58
borrowed_locals,
55
59
storage_to_remove,
56
60
}
@@ -75,31 +79,44 @@ fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet<Local> {
75
79
let mut fully_moved = BitSet :: new_filled ( body. local_decls . len ( ) ) ;
76
80
77
81
for ( _, rvalue, _) in ssa. assignments ( body) {
78
- let ( Rvalue :: Use ( Operand :: Copy ( place) | Operand :: Move ( place) )
79
- | Rvalue :: CopyForDeref ( place) ) = rvalue
80
- else {
82
+ let ( Rvalue :: Use ( Operand :: Copy ( place) ) | Rvalue :: CopyForDeref ( place) ) = rvalue else {
81
83
continue ;
82
84
} ;
83
85
84
86
let Some ( rhs) = place. as_local ( ) else { continue } ;
85
87
if !ssa. is_ssa ( rhs) {
86
88
continue ;
87
89
}
88
-
89
- if let Rvalue :: Use ( Operand :: Copy ( _) ) | Rvalue :: CopyForDeref ( _) = rvalue {
90
- fully_moved. remove ( rhs) ;
91
- }
90
+ fully_moved. remove ( rhs) ;
92
91
}
93
92
94
93
ssa. meet_copy_equivalence ( & mut fully_moved) ;
95
94
96
95
fully_moved
97
96
}
98
97
98
+ /// Finds all locals that are only assigned to once with a deterministic constant
99
+ #[ instrument( level = "trace" , skip( ssa, body) ) ]
100
+ fn const_locals < ' body , ' tcx > (
101
+ ssa : & ' body SsaLocals ,
102
+ body : & ' body Body < ' tcx > ,
103
+ ) -> FxIndexMap < Local , ConstOperand < ' tcx > > {
104
+ let mut const_locals = FxIndexMap :: default ( ) ;
105
+ for ( local, rvalue, _) in ssa. assignments ( body) {
106
+ let Rvalue :: Use ( Operand :: Constant ( val) ) = rvalue else { continue } ;
107
+ if !val. const_ . is_deterministic ( ) {
108
+ continue ;
109
+ }
110
+ const_locals. insert ( local, * * val) ;
111
+ }
112
+ const_locals
113
+ }
114
+
99
115
/// Utility to help performing substitution of `*pattern` by `target`.
100
116
struct Replacer < ' a , ' tcx > {
101
117
tcx : TyCtxt < ' tcx > ,
102
118
fully_moved : BitSet < Local > ,
119
+ const_locals : FxIndexMap < Local , ConstOperand < ' tcx > > ,
103
120
storage_to_remove : BitSet < Local > ,
104
121
borrowed_locals : BitSet < Local > ,
105
122
copy_classes : & ' a IndexSlice < Local , Local > ,
@@ -151,12 +168,15 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
151
168
}
152
169
153
170
fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , loc : Location ) {
154
- if let Operand :: Move ( place) = * operand
171
+ if let Operand :: Move ( place) = * operand {
155
172
// A move out of a projection of a copy is equivalent to a copy of the original projection.
156
- && !place. is_indirect_first_projection ( )
157
- && !self . fully_moved . contains ( place. local )
158
- {
159
- * operand = Operand :: Copy ( place) ;
173
+ if !place. is_indirect_first_projection ( ) && !self . fully_moved . contains ( place. local ) {
174
+ * operand = Operand :: Copy ( place) ;
175
+ } else if let Some ( local) = place. as_local ( )
176
+ && let Some ( val) = self . const_locals . get ( & local)
177
+ {
178
+ * operand = Operand :: Constant ( Box :: new ( * val) )
179
+ }
160
180
}
161
181
self . super_operand ( operand, loc) ;
162
182
}
0 commit comments