1//! Deduces supplementary parameter attributes from MIR.
2//!
3//! Deduced parameter attributes are those that can only be soundly determined by examining the
4//! body of the function instead of just the signature. These can be useful for optimization
5//! purposes on a best-effort basis. We compute them here and store them into the crate metadata so
6//! dependent crates can use them.
7//!
8//! Note that this *crucially* relies on codegen *not* doing any more MIR-level transformations
9//! after `optimized_mir`! We check for things that are *not* guaranteed to be preserved by MIR
10//! transforms, such as which local variables happen to be mutated.
1112use rustc_hiras hir;
13use rustc_hir::def_id::LocalDefId;
14use rustc_index::IndexVec;
15use rustc_middle::middle::deduced_param_attrs::{DeducedParamAttrs, UsageSummary};
16use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
17use rustc_middle::mir::*;
18use rustc_middle::ty::{self, Ty, TyCtxt};
19use rustc_session::config::OptLevel;
2021/// A visitor that determines how a return place and arguments are used inside MIR body.
22/// To determine whether a local is mutated we can't use the mutability field on LocalDecl
23/// because it has no meaning post-optimization.
24struct DeduceParamAttrs {
25/// Summarizes how a return place and arguments are used inside MIR body.
26usage: IndexVec<Local, UsageSummary>,
27}
2829impl DeduceParamAttrs {
30/// Returns a new DeduceParamAttrs instance.
31fn new(body: &Body<'_>) -> Self {
32let mut this =
33Self { usage: IndexVec::from_elem_n(UsageSummary::empty(), body.arg_count + 1) };
34// Code generation indicates that a return place is writable. To avoid setting both
35 // `readonly` and `writable` attributes, when return place is never written to, mark it as
36 // mutated.
37this.usage[RETURN_PLACE] |= UsageSummary::MUTATE;
38this39 }
4041/// Returns whether a local is the return place or an argument and returns its index.
42fn as_param(&self, local: Local) -> Option<Local> {
43if local.index() < self.usage.len() { Some(local) } else { None }
44 }
45}
4647impl<'tcx> Visitor<'tcx> for DeduceParamAttrs {
48fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
49// We're only interested in the return place or an argument.
50let Some(i) = self.as_param(place.local) else { return };
5152match context {
53// Not actually using the local.
54PlaceContext::NonUse(..) => {}
55// Neither mutated nor captured.
56_ if place.is_indirect_first_projection() => {}
57// This is a `Drop`. It could disappear at monomorphization, so mark it specially.
58PlaceContext::MutatingUse(MutatingUseContext::Drop)
59// Projection changes the place's type, so `needs_drop(local.ty)` is not
60 // `needs_drop(place.ty)`.
61if place.projection.is_empty() => {
62self.usage[i] |= UsageSummary::DROP;
63 }
64 PlaceContext::MutatingUse(
65 MutatingUseContext::Call66 | MutatingUseContext::Yield67 | MutatingUseContext::Drop68 | MutatingUseContext::Borrow69 | MutatingUseContext::RawBorrow) => {
70self.usage[i] |= UsageSummary::MUTATE;
71self.usage[i] |= UsageSummary::CAPTURE;
72 }
73 PlaceContext::MutatingUse(
74 MutatingUseContext::Store75 | MutatingUseContext::SetDiscriminant76 | MutatingUseContext::AsmOutput77 | MutatingUseContext::Projection) => {
78self.usage[i] |= UsageSummary::MUTATE;
79 }
80 | PlaceContext::NonMutatingUse(NonMutatingUseContext::RawBorrow) => {
81// Whether mutating though a `&raw const` is allowed is still undecided, so we
82 // disable any sketchy `readonly` optimizations for now.
83self.usage[i] |= UsageSummary::MUTATE;
84self.usage[i] |= UsageSummary::CAPTURE;
85 }
86 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow) => {
87// Not mutating if the parameter is `Freeze`.
88self.usage[i] |= UsageSummary::SHARED_BORROW;
89self.usage[i] |= UsageSummary::CAPTURE;
90 }
91// Not mutating, so it's fine.
92PlaceContext::NonMutatingUse(
93 NonMutatingUseContext::Inspect94 | NonMutatingUseContext::Copy95 | NonMutatingUseContext::Move96 | NonMutatingUseContext::FakeBorrow97 | NonMutatingUseContext::PlaceMention98 | NonMutatingUseContext::Projection) => {}
99 }
100 }
101102fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
103// OK, this is subtle. Suppose that we're trying to deduce whether `x` in `f` is read-only
104 // and we have the following:
105 //
106 // fn f(x: BigStruct) { g(x) }
107 // fn g(mut y: BigStruct) { y.foo = 1 }
108 //
109 // If, at the generated MIR level, `f` turned into something like:
110 //
111 // fn f(_1: BigStruct) -> () {
112 // let mut _0: ();
113 // bb0: {
114 // _0 = g(move _1) -> bb1;
115 // }
116 // ...
117 // }
118 //
119 // then it would be incorrect to mark `x` (i.e. `_1`) as `readonly`, because `g`'s write to
120 // its copy of the indirect parameter would actually be a write directly to the pointer that
121 // `f` passes. Note that function arguments are the only situation in which this problem can
122 // arise: every other use of `move` in MIR doesn't actually write to the value it moves
123 // from.
124match terminator.kind {
125 TerminatorKind::Call { ref args, .. } => {
126for arg in args {
127if let Operand::Move(place) = arg.node
128 && !place.is_indirect_first_projection()
129 && let Some(i) = self.as_param(place.local)
130 {
131self.usage[i] |= UsageSummary::MUTATE;
132self.usage[i] |= UsageSummary::CAPTURE;
133 }
134 }
135 }
136137// Like a call, but more conservative because the backend may introduce writes to an
138 // argument if the argument is passed as `PassMode::Indirect { on_stack: false, ... }`.
139TerminatorKind::TailCall { .. } => {
140for usage in self.usage.iter_mut() {
141*usage |= UsageSummary::MUTATE;
142*usage |= UsageSummary::CAPTURE;
143 }
144 }
145_ => {}
146 }
147148self.super_terminator(terminator, location);
149 }
150}
151152/// Returns true if values of a given type will never be passed indirectly, regardless of ABI.
153fn type_will_always_be_passed_directly(ty: Ty<'_>) -> bool {
154#[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
ty::Bool | ty::Char | ty::Float(..) | ty::Int(..) | ty::RawPtr(..) |
ty::Ref(..) | ty::Slice(..) | ty::Uint(..) => true,
_ => false,
}matches!(
155 ty.kind(),
156 ty::Bool
157 | ty::Char
158 | ty::Float(..)
159 | ty::Int(..)
160 | ty::RawPtr(..)
161 | ty::Ref(..)
162 | ty::Slice(..)
163 | ty::Uint(..)
164 )165}
166167/// Returns the deduced parameter attributes for a function.
168///
169/// Deduced parameter attributes are those that can only be soundly determined by examining the
170/// body of the function instead of just the signature. These can be useful for optimization
171/// purposes on a best-effort basis. We compute them here and store them into the crate metadata so
172/// dependent crates can use them.
173{}
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() || { false }
{
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("deduced_param_attrs",
"rustc_mir_transform::deduce_param_attrs",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/cea272fa356e94bd2ee2cadf376630aa0683867a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs"),
::tracing_core::__macro_support::Option::Some(173u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_transform::deduce_param_attrs"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("def_id")
}> =
::tracing::__macro_support::FieldName::new("def_id");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&def_id)
as &dyn ::tracing::field::Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[allow(clippy :: redundant_closure_call)]
let x =
(move ||
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy
:: needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: &'tcx [DeducedParamAttrs] =
loop {};
return __tracing_attr_fake_return;
}
{
if tcx.sess.opts.optimize == OptLevel::No ||
tcx.sess.opts.incremental.is_some() {
return &[];
}
if tcx.lang_items().freeze_trait().is_none() { return &[]; }
let fn_ty =
tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
if #[allow(non_exhaustive_omitted_patterns)] match fn_ty.kind()
{
ty::FnDef(..) => true,
_ => false,
} &&
fn_ty.fn_sig(tcx).inputs_and_output().skip_binder().iter().all(type_will_always_be_passed_directly)
{
return &[];
}
if !tcx.is_mir_available(def_id) { return &[]; }
if let hir::Constness::Const { always: true } =
tcx.constness(def_id) {
return &[];
}
let body: &Body<'tcx> = tcx.optimized_mir(def_id);
if body.spread_arg.is_some() { return &[]; }
let mut deduce = DeduceParamAttrs::new(body);
deduce.visit_body(body);
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/cea272fa356e94bd2ee2cadf376630aa0683867a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs:223",
"rustc_mir_transform::deduce_param_attrs",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/cea272fa356e94bd2ee2cadf376630aa0683867a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs"),
::tracing_core::__macro_support::Option::Some(223u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_transform::deduce_param_attrs"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("deduce.usage")
}> =
::tracing::__macro_support::FieldName::new("deduce.usage");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&deduce.usage)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};
let mut deduced_param_attrs: &[_] =
tcx.arena.alloc_from_iter(deduce.usage.into_iter().map(|usage|
DeducedParamAttrs { usage }));
while let Some((last, rest)) =
deduced_param_attrs.split_last() && last.is_default() {
deduced_param_attrs = rest;
}
deduced_param_attrs
}
})();
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/cea272fa356e94bd2ee2cadf376630aa0683867a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs:173",
"rustc_mir_transform::deduce_param_attrs",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/cea272fa356e94bd2ee2cadf376630aa0683867a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs"),
::tracing_core::__macro_support::Option::Some(173u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_transform::deduce_param_attrs"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("return")
}> =
::tracing::__macro_support::FieldName::new("return");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&x)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};
x;#[tracing::instrument(level = "trace", skip(tcx), ret)]174pub(super) fn deduced_param_attrs<'tcx>(
175 tcx: TyCtxt<'tcx>,
176 def_id: LocalDefId,
177) -> &'tcx [DeducedParamAttrs] {
178// This computation is unfortunately rather expensive, so don't do it unless we're optimizing.
179 // Also skip it in incremental mode.
180if tcx.sess.opts.optimize == OptLevel::No || tcx.sess.opts.incremental.is_some() {
181return &[];
182 }
183184// If the Freeze lang item isn't present, then don't bother.
185if tcx.lang_items().freeze_trait().is_none() {
186return &[];
187 }
188189// Codegen won't use this information for anything if all the function parameters are passed
190 // directly. Detect that and bail, for compilation speed.
191let fn_ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
192if matches!(fn_ty.kind(), ty::FnDef(..))
193 && fn_ty
194 .fn_sig(tcx)
195 .inputs_and_output()
196 .skip_binder()
197 .iter()
198 .all(type_will_always_be_passed_directly)
199 {
200return &[];
201 }
202203// Don't deduce any attributes for functions that have no MIR.
204if !tcx.is_mir_available(def_id) {
205return &[];
206 }
207208if let hir::Constness::Const { always: true } = tcx.constness(def_id) {
209// Comptime functions only exist during const eval and can never be passed
210 // to codegen.
211return &[];
212 }
213214// Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
215let body: &Body<'tcx> = tcx.optimized_mir(def_id);
216// Arguments spread at ABI level are currently unsupported.
217if body.spread_arg.is_some() {
218return &[];
219 }
220221let mut deduce = DeduceParamAttrs::new(body);
222 deduce.visit_body(body);
223tracing::trace!(?deduce.usage);
224225let mut deduced_param_attrs: &[_] = tcx
226 .arena
227 .alloc_from_iter(deduce.usage.into_iter().map(|usage| DeducedParamAttrs { usage }));
228229// Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
230 // default set of attributes, so we don't have to store them explicitly. Pop them off to save a
231 // few bytes in metadata.
232while let Some((last, rest)) = deduced_param_attrs.split_last()
233 && last.is_default()
234 {
235 deduced_param_attrs = rest;
236 }
237238 deduced_param_attrs
239}