1111
1212use rustc_hir:: def_id:: LocalDefId ;
1313use rustc_index:: IndexVec ;
14- use rustc_middle:: middle:: deduced_param_attrs:: { DeducedParamAttrs , DeducedReadOnlyParam } ;
14+ use rustc_middle:: middle:: deduced_param_attrs:: { DeducedParamAttrs , UsageSummary } ;
1515use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
1616use rustc_middle:: mir:: * ;
1717use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1818use rustc_session:: config:: OptLevel ;
1919
20- /// A visitor that determines which arguments have been mutated. We can't use the mutability field
21- /// on LocalDecl for this because it has no meaning post-optimization.
22- struct DeduceReadOnly {
23- /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl
24- /// 1). The bit is false if the argument may have been mutated or true if we know it hasn't
25- /// been up to the point we're at.
26- read_only : IndexVec < usize , DeducedReadOnlyParam > ,
20+ /// A visitor that determines how a return place and arguments are used inside MIR body.
21+ /// To determine whether a local is mutated we can't use the mutability field on LocalDecl
22+ /// because it has no meaning post-optimization.
23+ struct DeduceParamAttrs {
24+ /// Summarizes how a return place and arguments are used inside MIR body.
25+ usage : IndexVec < Local , UsageSummary > ,
2726}
2827
29- impl DeduceReadOnly {
30- /// Returns a new DeduceReadOnly instance.
31- fn new ( arg_count : usize ) -> Self {
32- Self { read_only : IndexVec :: from_elem_n ( DeducedReadOnlyParam :: empty ( ) , arg_count) }
28+ impl DeduceParamAttrs {
29+ /// Returns a new DeduceParamAttrs instance.
30+ fn new ( body : & Body < ' _ > ) -> Self {
31+ let mut this =
32+ Self { usage : IndexVec :: from_elem_n ( UsageSummary :: empty ( ) , body. arg_count + 1 ) } ;
33+ // Code generation indicates that a return place is writable. To avoid setting both
34+ // `readonly` and `writable` attributes, when return place is never written to, mark it as
35+ // mutated.
36+ this. usage [ RETURN_PLACE ] |= UsageSummary :: MUTATE ;
37+ this
3338 }
3439
35- /// Returns whether the given local is a parameter and its index.
36- fn as_param ( & self , local : Local ) -> Option < usize > {
37- // Locals and parameters are shifted by `RETURN_PLACE`.
38- let param_index = local. as_usize ( ) . checked_sub ( 1 ) ?;
39- if param_index < self . read_only . len ( ) { Some ( param_index) } else { None }
40+ /// Returns whether a local is the return place or an argument and returns its index.
41+ fn as_param ( & self , local : Local ) -> Option < Local > {
42+ if local. index ( ) < self . usage . len ( ) { Some ( local) } else { None }
4043 }
4144}
4245
43- impl < ' tcx > Visitor < ' tcx > for DeduceReadOnly {
46+ impl < ' tcx > Visitor < ' tcx > for DeduceParamAttrs {
4447 fn visit_place ( & mut self , place : & Place < ' tcx > , context : PlaceContext , _location : Location ) {
45- // We're only interested in arguments .
46- let Some ( param_index ) = self . as_param ( place. local ) else { return } ;
48+ // We're only interested in the return place or an argument .
49+ let Some ( i ) = self . as_param ( place. local ) else { return } ;
4750
4851 match context {
49- // Not mutating, so it's fine .
52+ // Not actually using the local .
5053 PlaceContext :: NonUse ( ..) => { }
51- // Dereference is not a mutation .
54+ // Neither mutated nor captured .
5255 _ if place. is_indirect_first_projection ( ) => { }
5356 // This is a `Drop`. It could disappear at monomorphization, so mark it specially.
5457 PlaceContext :: MutatingUse ( MutatingUseContext :: Drop )
5558 // Projection changes the place's type, so `needs_drop(local.ty)` is not
5659 // `needs_drop(place.ty)`.
5760 if place. projection . is_empty ( ) => {
58- self . read_only [ param_index] |= DeducedReadOnlyParam :: IF_NO_DROP ;
61+ self . usage [ i] |= UsageSummary :: DROP ;
62+ }
63+ PlaceContext :: MutatingUse (
64+ MutatingUseContext :: Call
65+ | MutatingUseContext :: Yield
66+ | MutatingUseContext :: Drop
67+ | MutatingUseContext :: Borrow
68+ | MutatingUseContext :: RawBorrow ) => {
69+ self . usage [ i] |= UsageSummary :: MUTATE ;
70+ self . usage [ i] |= UsageSummary :: CAPTURE ;
71+ }
72+ PlaceContext :: MutatingUse (
73+ MutatingUseContext :: Store
74+ | MutatingUseContext :: SetDiscriminant
75+ | MutatingUseContext :: AsmOutput
76+ | MutatingUseContext :: Projection
77+ | MutatingUseContext :: Retag ) => {
78+ self . usage [ i] |= UsageSummary :: MUTATE ;
5979 }
60- // This is a mutation, so mark it as such.
61- PlaceContext :: MutatingUse ( ..)
62- // Whether mutating though a `&raw const` is allowed is still undecided, so we
63- // disable any sketchy `readonly` optimizations for now.
6480 | PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: RawBorrow ) => {
65- self . read_only [ param_index] |= DeducedReadOnlyParam :: MUTATED ;
81+ // Whether mutating though a `&raw const` is allowed is still undecided, so we
82+ // disable any sketchy `readonly` optimizations for now.
83+ self . usage [ i] |= UsageSummary :: MUTATE ;
84+ self . usage [ i] |= UsageSummary :: CAPTURE ;
6685 }
67- // Not mutating if the parameter is `Freeze`.
6886 PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: SharedBorrow ) => {
69- self . read_only [ param_index] |= DeducedReadOnlyParam :: IF_FREEZE ;
87+ // Not mutating if the parameter is `Freeze`.
88+ self . usage [ i] |= UsageSummary :: SHARED_BORROW ;
89+ self . usage [ i] |= UsageSummary :: CAPTURE ;
7090 }
7191 // Not mutating, so it's fine.
72- PlaceContext :: NonMutatingUse ( ..) => { }
92+ PlaceContext :: NonMutatingUse (
93+ NonMutatingUseContext :: Inspect
94+ | NonMutatingUseContext :: Copy
95+ | NonMutatingUseContext :: Move
96+ | NonMutatingUseContext :: FakeBorrow
97+ | NonMutatingUseContext :: PlaceMention
98+ | NonMutatingUseContext :: Projection ) => { }
7399 }
74100 }
75101
@@ -98,11 +124,11 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
98124 if let TerminatorKind :: Call { ref args, .. } = terminator. kind {
99125 for arg in args {
100126 if let Operand :: Move ( place) = arg. node
101- // We're only interested in arguments.
102- && let Some ( param_index) = self . as_param ( place. local )
103127 && !place. is_indirect_first_projection ( )
128+ && let Some ( i) = self . as_param ( place. local )
104129 {
105- self . read_only [ param_index] |= DeducedReadOnlyParam :: MUTATED ;
130+ self . usage [ i] |= UsageSummary :: MUTATE ;
131+ self . usage [ i] |= UsageSummary :: CAPTURE ;
106132 }
107133 }
108134 } ;
@@ -154,10 +180,9 @@ pub(super) fn deduced_param_attrs<'tcx>(
154180 if matches ! ( fn_ty. kind( ) , ty:: FnDef ( ..) )
155181 && fn_ty
156182 . fn_sig ( tcx)
157- . inputs ( )
183+ . inputs_and_output ( )
158184 . skip_binder ( )
159185 . iter ( )
160- . cloned ( )
161186 . all ( type_will_always_be_passed_directly)
162187 {
163188 return & [ ] ;
@@ -170,13 +195,13 @@ pub(super) fn deduced_param_attrs<'tcx>(
170195
171196 // Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
172197 let body: & Body < ' tcx > = tcx. optimized_mir ( def_id) ;
173- let mut deduce_read_only = DeduceReadOnly :: new ( body. arg_count ) ;
174- deduce_read_only . visit_body ( body) ;
175- tracing:: trace!( ?deduce_read_only . read_only ) ;
198+ let mut deduce = DeduceParamAttrs :: new ( body) ;
199+ deduce . visit_body ( body) ;
200+ tracing:: trace!( ?deduce . usage ) ;
176201
177- let mut deduced_param_attrs: & [ _ ] = tcx. arena . alloc_from_iter (
178- deduce_read_only . read_only . into_iter ( ) . map ( |read_only| DeducedParamAttrs { read_only } ) ,
179- ) ;
202+ let mut deduced_param_attrs: & [ _ ] = tcx
203+ . arena
204+ . alloc_from_iter ( deduce . usage . into_iter ( ) . map ( |usage| DeducedParamAttrs { usage } ) ) ;
180205
181206 // Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
182207 // default set of attributes, so we don't have to store them explicitly. Pop them off to save a
0 commit comments