Skip to content

Commit c188ab3

Browse files
committed
deduce readonly attribute for generic parameters
1 parent 2b285cd commit c188ab3

File tree

3 files changed

+61
-49
lines changed

3 files changed

+61
-49
lines changed

compiler/rustc_middle/src/ty/context.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3300,6 +3300,8 @@ pub struct DeducedParamAttrs {
33003300
/// The parameter is marked immutable in the function and contains no `UnsafeCell` (i.e. its
33013301
/// type is freeze).
33023302
pub read_only: bool,
3303+
pub requires_freeze: bool,
3304+
pub requires_nop_drop: bool,
33033305
}
33043306

33053307
pub fn provide(providers: &mut Providers) {

compiler/rustc_mir_transform/src/deduce_param_attrs.rs

+53-48
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,65 @@
88
use rustc_hir::def_id::LocalDefId;
99
use rustc_index::bit_set::DenseBitSet;
1010
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+
Body, Local, Location, Operand, Place, RETURN_PLACE, Terminator, TerminatorKind,
13+
};
1214
use rustc_middle::ty::{self, DeducedParamAttrs, Ty, TyCtxt};
1315
use rustc_session::config::OptLevel;
16+
use tracing::instrument;
1417

1518
/// A visitor that determines which arguments have been mutated. We can't use the mutability field
1619
/// on LocalDecl for this because it has no meaning post-optimization.
1720
struct DeduceReadOnly {
1821
/// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl
1922
/// 1). The bit is true if the argument may have been mutated or false if we know it hasn't
2023
/// been up to the point we're at.
21-
mutable_args: DenseBitSet<usize>,
24+
read_only: DenseBitSet<usize>,
25+
requires_freeze: DenseBitSet<usize>,
26+
requires_nop_drop: DenseBitSet<usize>,
2227
}
2328

2429
impl DeduceReadOnly {
2530
/// Returns a new DeduceReadOnly instance.
2631
fn new(arg_count: usize) -> Self {
27-
Self { mutable_args: DenseBitSet::new_empty(arg_count) }
32+
Self {
33+
read_only: DenseBitSet::new_filled(arg_count),
34+
requires_freeze: DenseBitSet::new_empty(arg_count),
35+
requires_nop_drop: DenseBitSet::new_empty(arg_count),
36+
}
37+
}
38+
39+
fn arg_index(&self, local: Local) -> Option<usize> {
40+
if local == RETURN_PLACE || local.index() > self.read_only.domain_size() {
41+
None
42+
} else {
43+
Some(local.index() - 1)
44+
}
2845
}
2946
}
3047

3148
impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
3249
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
33-
// We're only interested in arguments.
34-
if place.local == RETURN_PLACE || place.local.index() > self.mutable_args.domain_size() {
50+
if place.is_indirect() {
3551
return;
3652
}
37-
38-
let mark_as_mutable = match context {
53+
// We're only interested in arguments.
54+
let Some(arg_index) = self.arg_index(place.local) else { return };
55+
match context {
3956
PlaceContext::MutatingUse(..) => {
4057
// This is a mutation, so mark it as such.
41-
true
58+
self.read_only.remove(arg_index);
4259
}
4360
PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => {
4461
// 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()
62+
// disable any sketchy `readonly` optimizations for now.
63+
self.read_only.remove(arg_index);
4964
}
50-
PlaceContext::NonMutatingUse(..) | PlaceContext::NonUse(..) => {
51-
// Not mutating, so it's fine.
52-
false
65+
PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
66+
self.requires_freeze.insert(arg_index);
5367
}
68+
PlaceContext::NonMutatingUse(..) | PlaceContext::NonUse(..) => {}
5469
};
55-
56-
if mark_as_mutable {
57-
self.mutable_args.insert(place.local.index() - 1);
58-
}
5970
}
6071

6172
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
@@ -83,18 +94,23 @@ impl<'tcx> Visitor<'tcx> for DeduceReadOnly {
8394
if let TerminatorKind::Call { ref args, .. } = terminator.kind {
8495
for arg in args {
8596
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-
{
97+
if place.is_indirect() {
9198
continue;
9299
}
93-
94-
self.mutable_args.insert(local.index() - 1);
100+
if let Some(arg_index) = self.arg_index(place.local) {
101+
self.read_only.remove(arg_index);
102+
}
95103
}
96104
}
97105
};
106+
if let TerminatorKind::Drop { place, .. } = terminator.kind {
107+
if let Some(local) = place.as_local()
108+
&& let Some(arg_index) = self.arg_index(local)
109+
{
110+
self.requires_nop_drop.insert(arg_index);
111+
return;
112+
}
113+
}
98114

99115
self.super_terminator(terminator, location);
100116
}
@@ -121,6 +137,7 @@ fn type_will_always_be_passed_directly(ty: Ty<'_>) -> bool {
121137
/// body of the function instead of just the signature. These can be useful for optimization
122138
/// purposes on a best-effort basis. We compute them here and store them into the crate metadata so
123139
/// dependent crates can use them.
140+
#[instrument(level = "debug", ret, skip(tcx))]
124141
pub(super) fn deduced_param_attrs<'tcx>(
125142
tcx: TyCtxt<'tcx>,
126143
def_id: LocalDefId,
@@ -158,31 +175,19 @@ pub(super) fn deduced_param_attrs<'tcx>(
158175

159176
// Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
160177
let body: &Body<'tcx> = tcx.optimized_mir(def_id);
161-
let mut deduce_read_only = DeduceReadOnly::new(body.arg_count);
162-
deduce_read_only.visit_body(body);
178+
let mut deduce = DeduceReadOnly::new(body.arg_count);
179+
deduce.visit_body(body);
163180

164181
// Set the `readonly` attribute for every argument that we concluded is immutable and that
165182
// contains no UnsafeCells.
166-
//
167-
// FIXME: This is overly conservative around generic parameters: `is_freeze()` will always
168-
// return false for them. For a description of alternatives that could do a better job here,
169-
// see [1].
170-
//
171-
// [1]: https://github.com/rust-lang/rust/pull/103172#discussion_r999139997
172-
let typing_env = body.typing_env(tcx);
173-
let mut deduced_param_attrs = tcx.arena.alloc_from_iter(
174-
body.local_decls.iter().skip(1).take(body.arg_count).enumerate().map(
175-
|(arg_index, local_decl)| DeducedParamAttrs {
176-
read_only: !deduce_read_only.mutable_args.contains(arg_index)
177-
// We must normalize here to reveal opaques and normalize
178-
// their generic parameters, otherwise we'll see exponential
179-
// blow-up in compile times: #113372
180-
&& tcx
181-
.normalize_erasing_regions(typing_env, local_decl.ty)
182-
.is_freeze(tcx, typing_env),
183-
},
184-
),
185-
);
183+
let mut deduced_param_attrs = tcx.arena.alloc_from_iter((0..body.arg_count).map(|arg_index| {
184+
let read_only = deduce.read_only.contains(arg_index);
185+
DeducedParamAttrs {
186+
read_only,
187+
requires_freeze: read_only && deduce.requires_freeze.contains(arg_index),
188+
requires_nop_drop: read_only && deduce.requires_nop_drop.contains(arg_index),
189+
}
190+
}));
186191

187192
// Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
188193
// default set of attributes, so we don't have to store them explicitly. Pop them off to save a

compiler/rustc_ty_utils/src/abi.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,12 @@ fn fn_abi_adjust_for_abi<'tcx>(
712712
// we can't deduce any parameters for, so make sure the argument index is in
713713
// bounds.
714714
if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) {
715-
if deduced_param_attrs.read_only {
715+
if deduced_param_attrs.read_only
716+
&& (!deduced_param_attrs.requires_freeze
717+
|| arg.layout.ty.is_freeze(tcx, cx.typing_env))
718+
&& (!deduced_param_attrs.requires_nop_drop
719+
|| !arg.layout.ty.needs_drop(tcx, cx.typing_env))
720+
{
716721
attrs.regular.insert(ArgAttribute::ReadOnly);
717722
debug!("added deduced read-only attribute");
718723
}

0 commit comments

Comments
 (0)