Skip to content

Commit f0f5e19

Browse files
lcnrtmiasko
andcommitted
Inline trivial leaf functions
Co-authored-by: Tomasz Miąsko <[email protected]>
1 parent cf78d93 commit f0f5e19

File tree

8 files changed

+121
-15
lines changed

8 files changed

+121
-15
lines changed

compiler/rustc_metadata/src/rmeta/decoder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
11741174
}
11751175
}
11761176

1177+
fn get_is_trivial_mir(&self, id: DefIndex) -> bool {
1178+
self.root.tables.is_trivial_mir.get(self, id).is_some()
1179+
}
1180+
11771181
fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> {
11781182
self.root
11791183
.tables

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
117117
bug!("coerce_unsized_info: `{:?}` is missing its info", def_id);
118118
})
119119
}
120+
is_trivial_mir => { cdata.get_is_trivial_mir(def_id.index) }
120121
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
121122
mir_for_ctfe => { tcx.arena.alloc(cdata.get_mir_for_ctfe(tcx, def_id.index)) }
122123
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,12 @@ impl EncodeContext<'a, 'tcx> {
11641164
debug!("EntryBuilder::encode_optimized_mir({:?})", def_id);
11651165
record!(self.tables.mir[def_id.to_def_id()] <- self.tcx.optimized_mir(def_id));
11661166

1167+
if self.tcx.is_trivial_mir(def_id) {
1168+
// We don't store anything if `is_trivial_mir` is `false`
1169+
// so we can use a unit type here.
1170+
self.tables.is_trivial_mir.set(def_id.local_def_index, ());
1171+
}
1172+
11671173
let unused = self.tcx.unused_generic_params(def_id);
11681174
if !unused.is_empty() {
11691175
record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused);

compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ define_tables! {
301301
super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
302302
// As an optimization, a missing entry indicates an empty `&[]`.
303303
explicit_item_bounds: Table<DefIndex, Lazy!([(ty::Predicate<'tcx>, Span)])>,
304+
is_trivial_mir: Table<DefIndex, ()>,
304305
mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
305306
mir_for_ctfe: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
306307
promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,

compiler/rustc_metadata/src/rmeta/table.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_index::vec::Idx;
44
use rustc_serialize::opaque::Encoder;
55
use std::convert::TryInto;
66
use std::marker::PhantomData;
7-
use std::num::NonZeroUsize;
7+
use std::num::{NonZeroU8, NonZeroUsize};
88
use tracing::debug;
99

1010
/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
@@ -75,6 +75,30 @@ impl FixedSizeEncoding for u32 {
7575
}
7676
}
7777

78+
impl FixedSizeEncoding for Option<NonZeroU8> {
79+
fixed_size_encoding_byte_len_and_defaults!(1);
80+
81+
fn from_bytes(b: &[u8]) -> Self {
82+
NonZeroU8::new(b[0])
83+
}
84+
85+
fn write_to_bytes(self, b: &mut [u8]) {
86+
b[0] = self.map_or(0, |x| x.get());
87+
}
88+
}
89+
90+
impl FixedSizeEncoding for Option<()> {
91+
fixed_size_encoding_byte_len_and_defaults!(Option::<NonZeroU8>::BYTE_LEN);
92+
93+
fn from_bytes(b: &[u8]) -> Self {
94+
Option::<NonZeroU8>::from_bytes(b).map(|_| ())
95+
}
96+
97+
fn write_to_bytes(self, b: &mut [u8]) {
98+
self.map(|()| NonZeroU8::new(1).unwrap()).write_to_bytes(b)
99+
}
100+
}
101+
78102
// NOTE(eddyb) there could be an impl for `usize`, which would enable a more
79103
// generic `Lazy<T>` impl, but in the general case we might not need / want to
80104
// fit every `usize` in `u32`.

compiler/rustc_middle/src/query/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@ rustc_queries! {
339339
}
340340
}
341341

342+
query is_trivial_mir(key: DefId) -> bool {
343+
desc { |tcx| "checking if MIR for `{}` is trivial", tcx.def_path_str(key) }
344+
cache_on_disk_if { key.is_local() }
345+
}
346+
342347
/// MIR after our optimization passes have run. This is MIR that is ready
343348
/// for codegen. This is also the only query that can fetch non-local MIR, at present.
344349
query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {

compiler/rustc_mir/src/transform/inline.rs

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use rustc_attr as attr;
44
use rustc_hir as hir;
5+
use rustc_hir::def_id::DefId;
56
use rustc_index::bit_set::BitSet;
67
use rustc_index::vec::Idx;
78
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
@@ -37,10 +38,6 @@ struct CallSite<'tcx> {
3738

3839
impl<'tcx> MirPass<'tcx> for Inline {
3940
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
40-
if tcx.sess.opts.debugging_opts.mir_opt_level < 2 {
41-
return;
42-
}
43-
4441
if tcx.sess.opts.debugging_opts.instrument_coverage {
4542
// Since `Inline` happens after `InstrumentCoverage`, the function-specific coverage
4643
// counters can be invalidated, such as by merging coverage counter statements from
@@ -50,6 +47,13 @@ impl<'tcx> MirPass<'tcx> for Inline {
5047
return;
5148
}
5249

50+
if body.generator_kind.is_some() {
51+
// Avoid inlining into generators, since their `optimized_mir` is used for layout
52+
// computation, which can create a cycle, even when no attempt is made to inline
53+
// the function in the other direction.
54+
return;
55+
}
56+
5357
if inline(tcx, body) {
5458
debug!("running simplify cfg on {:?}", body.source);
5559
CfgSimplifier::new(body).simplify();
@@ -104,7 +108,7 @@ impl Inliner<'tcx> {
104108
Some(it) => it,
105109
};
106110

107-
if !self.is_mir_available(&callsite.callee, caller_body) {
111+
if !self.is_mir_available(&callsite.callee) {
108112
debug!("MIR unavailable {}", callsite.callee);
109113
continue;
110114
}
@@ -137,11 +141,27 @@ impl Inliner<'tcx> {
137141
}
138142
}
139143

140-
fn is_mir_available(&self, callee: &Instance<'tcx>, caller_body: &Body<'tcx>) -> bool {
141-
if let InstanceDef::Item(_) = callee.def {
142-
if !self.tcx.is_mir_available(callee.def_id()) {
143-
return false;
144-
}
144+
fn is_mir_available(&self, callee: &Instance<'tcx>) -> bool {
145+
match callee.def {
146+
InstanceDef::Virtual(..) | InstanceDef::Intrinsic(..) => return false,
147+
148+
InstanceDef::VtableShim(..)
149+
| InstanceDef::ReifyShim(..)
150+
| InstanceDef::FnPtrShim(..)
151+
| InstanceDef::ClosureOnceShim { .. }
152+
| InstanceDef::DropGlue(..)
153+
| InstanceDef::CloneShim(..) => return true,
154+
155+
InstanceDef::Item(_) => {}
156+
};
157+
158+
if !self.tcx.is_mir_available(callee.def_id()) {
159+
return false;
160+
}
161+
162+
if self.tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
163+
// Only inline trivial functions by default.
164+
return self.tcx.is_trivial_mir(callee.def_id());
145165
}
146166

147167
if let Some(callee_def_id) = callee.def_id().as_local() {
@@ -153,9 +173,7 @@ impl Inliner<'tcx> {
153173
// since their `optimized_mir` is used for layout computation, which can
154174
// create a cycle, even when no attempt is made to inline the function
155175
// in the other direction.
156-
!self.tcx.dep_graph.is_fully_enabled()
157-
&& self.hir_id < callee_hir_id
158-
&& caller_body.generator_kind.is_none()
176+
!self.tcx.dep_graph.is_fully_enabled() && self.hir_id < callee_hir_id
159177
} else {
160178
// This cannot result in a cycle since the callee MIR is from another crate
161179
// and is already optimized.
@@ -885,3 +903,48 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
885903
}
886904
}
887905
}
906+
907+
pub fn is_trivial_mir(tcx: TyCtxt<'tcx>, did: DefId) -> bool {
908+
debug!("is_trivial_mir({:?})", did);
909+
if tcx.is_constructor(did) {
910+
debug!("is_trivial_mir = true (constructor)");
911+
return true;
912+
}
913+
914+
use rustc_hir::def::DefKind;
915+
if !matches!(tcx.def_kind(did), DefKind::Fn | DefKind::AssocFn) {
916+
debug!("is_trivial_mir = false (not a function)");
917+
// Only inline functions, don't look at constants here.
918+
return false;
919+
}
920+
921+
if !did.is_local() {
922+
// This branch is only taken if no `optimized_mir` is available for
923+
// an extern crate, as `is_trivial_mir` has otherwise been encoded.
924+
debug!("is_trivial_mir = false (no MIR available)");
925+
return false;
926+
};
927+
928+
let body = tcx
929+
.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did.expect_local()))
930+
.borrow();
931+
932+
for bb in body.basic_blocks() {
933+
let terminator = bb.terminator();
934+
if let TerminatorKind::Call { func, .. } = &terminator.kind {
935+
let func_ty = func.ty(body.local_decls(), tcx);
936+
if let ty::FnDef(..) = *func_ty.kind() {
937+
let fn_sig = func_ty.fn_sig(tcx);
938+
if fn_sig.abi() == Abi::RustIntrinsic || fn_sig.abi() == Abi::PlatformIntrinsic {
939+
continue;
940+
}
941+
}
942+
943+
debug!("is_trivial_mir = false (function call)");
944+
return false;
945+
}
946+
}
947+
948+
debug!("is_trivial_mir = true");
949+
true
950+
}

compiler/rustc_mir/src/transform/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub(crate) fn provide(providers: &mut Providers) {
7171
},
7272
mir_promoted,
7373
mir_drops_elaborated_and_const_checked,
74+
is_trivial_mir: inline::is_trivial_mir,
7475
mir_for_ctfe,
7576
mir_for_ctfe_of_const_arg,
7677
optimized_mir,
@@ -555,7 +556,8 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
555556
// constructors.
556557
return shim::build_adt_ctor(tcx, did.to_def_id());
557558
}
558-
559+
// `is_trivial_mir` uses `mir_drops_elaborated_and_const_checked` so run that first.
560+
tcx.ensure().is_trivial_mir(did.to_def_id());
559561
match tcx.hir().body_const_context(did) {
560562
// Run the `mir_for_ctfe` query, which depends on `mir_drops_elaborated_and_const_checked`
561563
// which we are going to steal below. Thus we need to run `mir_for_ctfe` first, so it

0 commit comments

Comments
 (0)