|
8 | 8 | use rustc_hir::def_id::LocalDefId; |
9 | 9 | use rustc_index::bit_set::DenseBitSet; |
10 | 10 | use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; |
11 | | -use rustc_middle::mir::{Body, Location, Operand, Place, RETURN_PLACE, Terminator, TerminatorKind}; |
| 11 | +use rustc_middle::mir::*; |
12 | 12 | use rustc_middle::ty::{self, DeducedParamAttrs, Ty, TyCtxt}; |
13 | 13 | use rustc_session::config::OptLevel; |
14 | 14 |
|
15 | 15 | /// A visitor that determines which arguments have been mutated. We can't use the mutability field |
16 | 16 | /// on LocalDecl for this because it has no meaning post-optimization. |
17 | 17 | struct DeduceReadOnly { |
18 | 18 | /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl |
19 | | - /// 1). The bit is true if the argument may have been mutated or false if we know it hasn't |
| 19 | + /// 1). The bit is false if the argument may have been mutated or true if we know it hasn't |
20 | 20 | /// been up to the point we're at. |
21 | | - mutable_args: DenseBitSet<usize>, |
| 21 | + read_only: DenseBitSet<usize>, |
22 | 22 | } |
23 | 23 |
|
24 | 24 | impl DeduceReadOnly { |
25 | 25 | /// Returns a new DeduceReadOnly instance. |
26 | 26 | fn new(arg_count: usize) -> Self { |
27 | | - Self { mutable_args: DenseBitSet::new_empty(arg_count) } |
| 27 | + Self { read_only: DenseBitSet::new_filled(arg_count) } |
| 28 | + } |
| 29 | + |
| 30 | + /// Returns whether the given local is a parameter and its index. |
| 31 | + fn as_param(&self, local: Local) -> Option<usize> { |
| 32 | + // Locals and parameters are shifted by `RETURN_PLACE`. |
| 33 | + let param_index = local.as_usize().checked_sub(1)?; |
| 34 | + if param_index < self.read_only.domain_size() { Some(param_index) } else { None } |
28 | 35 | } |
29 | 36 | } |
30 | 37 |
|
31 | 38 | impl<'tcx> Visitor<'tcx> for DeduceReadOnly { |
32 | 39 | fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { |
33 | 40 | // We're only interested in arguments. |
34 | | - if place.local == RETURN_PLACE || place.local.index() > self.mutable_args.domain_size() { |
35 | | - return; |
36 | | - } |
| 41 | + let Some(param_index) = self.as_param(place.local) else { return }; |
37 | 42 |
|
38 | 43 | let mark_as_mutable = match context { |
39 | | - PlaceContext::MutatingUse(..) => { |
40 | | - // This is a mutation, so mark it as such. |
41 | | - true |
42 | | - } |
43 | | - PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => { |
44 | | - // Whether mutating though a `&raw const` is allowed is still undecided, so we |
45 | | - // disable any sketchy `readonly` optimizations for now. But we only need to do |
46 | | - // this if the pointer would point into the argument. IOW: for indirect places, |
47 | | - // like `&raw (*local).field`, this surely cannot mutate `local`. |
48 | | - !place.is_indirect() |
49 | | - } |
50 | | - PlaceContext::NonMutatingUse(..) | PlaceContext::NonUse(..) => { |
51 | | - // Not mutating, so it's fine. |
52 | | - false |
53 | | - } |
| 44 | + // Not mutating, so it's fine. |
| 45 | + PlaceContext::NonUse(..) => false, |
| 46 | + // Dereference is not a mutation. |
| 47 | + _ if place.is_indirect_first_projection() => false, |
| 48 | + // This is a mutation, so mark it as such. |
| 49 | + PlaceContext::MutatingUse(..) => true, |
| 50 | + // Whether mutating though a `&raw const` is allowed is still undecided, so we |
| 51 | + // disable any sketchy `readonly` optimizations for now. But we only need to do |
| 52 | + // this if the pointer would point into the argument. IOW: for indirect places, |
| 53 | + // like `&raw (*local).field`, this surely cannot mutate `local`. |
| 54 | + PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => true, |
| 55 | + // Not mutating, so it's fine. |
| 56 | + PlaceContext::NonMutatingUse(..) => false, |
54 | 57 | }; |
55 | 58 |
|
56 | 59 | if mark_as_mutable { |
57 | | - self.mutable_args.insert(place.local.index() - 1); |
| 60 | + self.read_only.remove(param_index); |
58 | 61 | } |
59 | 62 | } |
60 | 63 |
|
@@ -82,16 +85,12 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly { |
82 | 85 | // from. |
83 | 86 | if let TerminatorKind::Call { ref args, .. } = terminator.kind { |
84 | 87 | for arg in args { |
85 | | - if let Operand::Move(place) = arg.node { |
86 | | - let local = place.local; |
87 | | - if place.is_indirect() |
88 | | - || local == RETURN_PLACE |
89 | | - || local.index() > self.mutable_args.domain_size() |
90 | | - { |
91 | | - continue; |
92 | | - } |
93 | | - |
94 | | - self.mutable_args.insert(local.index() - 1); |
| 88 | + if let Operand::Move(place) = arg.node |
| 89 | + // We're only interested in arguments. |
| 90 | + && let Some(param_index) = self.as_param(place.local) |
| 91 | + && !place.is_indirect_first_projection() |
| 92 | + { |
| 93 | + self.read_only.remove(param_index); |
95 | 94 | } |
96 | 95 | } |
97 | 96 | }; |
@@ -170,7 +169,7 @@ pub(super) fn deduced_param_attrs<'tcx>( |
170 | 169 | // |
171 | 170 | // [1]: https://github.com/rust-lang/rust/pull/103172#discussion_r999139997 |
172 | 171 | let mut deduced_param_attrs = tcx.arena.alloc_from_iter((0..body.arg_count).map(|arg_index| { |
173 | | - DeducedParamAttrs { read_only: !deduce_read_only.mutable_args.contains(arg_index) } |
| 172 | + DeducedParamAttrs { read_only: deduce_read_only.read_only.contains(arg_index) } |
174 | 173 | })); |
175 | 174 |
|
176 | 175 | // Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the |
|
0 commit comments