Skip to content

Commit b9beeae

Browse files
committed
Increase the precision of the exportable MIR check
1 parent a34cead commit b9beeae

28 files changed

+315
-205
lines changed

compiler/rustc_metadata/src/rmeta/encoder.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
15541554
self.tables.unused_generic_params.set(def_id.local_def_index, unused);
15551555
}
15561556

1557+
// Encode promoted_mir for all functions that we inlined
1558+
let prev_len = tcx.inlined_internal_defs.lock().len();
1559+
for def_id in tcx.inlined_internal_defs.lock().clone() {
1560+
record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id));
1561+
}
1562+
assert_eq!(tcx.inlined_internal_defs.lock().len(), prev_len);
1563+
15571564
// Encode all the deduced parameter attributes for everything that has MIR, even for items
15581565
// that can't be inlined. But don't if we aren't optimizing in non-incremental mode, to
15591566
// save the query traffic.

compiler/rustc_middle/src/ty/context.rs

+3
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ pub struct GlobalCtxt<'tcx> {
558558

559559
/// Stores memory for globals (statics/consts).
560560
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
561+
562+
pub inlined_internal_defs: Lock<FxHashSet<LocalDefId>>,
561563
}
562564

563565
impl<'tcx> GlobalCtxt<'tcx> {
@@ -706,6 +708,7 @@ impl<'tcx> TyCtxt<'tcx> {
706708
new_solver_evaluation_cache: Default::default(),
707709
data_layout,
708710
alloc_map: Lock::new(interpret::AllocMap::new()),
711+
inlined_internal_defs: Default::default(),
709712
}
710713
}
711714

compiler/rustc_mir_transform/src/inline.rs

+83-10
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ impl<'tcx> Inliner<'tcx> {
146146
debug!("inlined {}", callsite.callee);
147147
self.changed = true;
148148

149+
if !matches!(callsite.callee.def, InstanceDef::CloneShim(..)) {
150+
if let Some(def_id) = callsite.callee.def_id().as_local() {
151+
self.tcx.inlined_internal_defs.lock().insert(def_id);
152+
}
153+
}
154+
149155
self.history.push(callsite.callee.def_id());
150156
self.process_blocks(caller_body, new_blocks);
151157
self.history.pop();
@@ -184,6 +190,7 @@ impl<'tcx> Inliner<'tcx> {
184190

185191
self.check_mir_is_available(caller_body, &callsite.callee)?;
186192
let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?;
193+
self.check_mir_is_exportable(&callsite.callee, callee_attrs, callee_body)?;
187194
self.check_mir_body(callsite, callee_body, callee_attrs)?;
188195

189196
if !self.tcx.consider_optimizing(|| {
@@ -353,6 +360,44 @@ impl<'tcx> Inliner<'tcx> {
353360
None
354361
}
355362

363+
fn check_mir_is_exportable(
364+
&self,
365+
callee: &Instance<'tcx>,
366+
callee_attrs: &CodegenFnAttrs,
367+
callee_body: &Body<'tcx>,
368+
) -> Result<(), &'static str> {
369+
// If the callee is not local, then it must be exportable because we passed the check for
370+
// if MIR is available.
371+
if !callee.def_id().is_local() {
372+
return Ok(());
373+
}
374+
if self.tcx.is_constructor(callee.def_id()) {
375+
return Ok(());
376+
}
377+
if callee_attrs.requests_inline() {
378+
return Ok(());
379+
}
380+
let is_generic = callee.substs.non_erasable_generics().next().is_some();
381+
if is_generic {
382+
return Ok(());
383+
}
384+
385+
// So now we are trying to inline a function from another crate which is not inline and is
386+
// not a generic. This might work. But if this pulls in a symbol from the other crater, we
387+
// will fail to link.
388+
// So this is our heuritic for handling this:
389+
// If this function has any calls in it, that might reference an internal symbol.
390+
// If this function contains a pointer constant, that might be a reference to an internal
391+
// static.
392+
// So if either of those conditions are met, we cannot inline this.
393+
if !contains_pointer_constant(callee_body) {
394+
debug!("Has no pointer constants, must be exportable");
395+
return Ok(());
396+
}
397+
398+
Err("not exported")
399+
}
400+
356401
/// Returns an error if inlining is not possible based on codegen attributes alone. A success
357402
/// indicates that inlining decision should be based on other criteria.
358403
fn check_codegen_attributes(
@@ -364,16 +409,6 @@ impl<'tcx> Inliner<'tcx> {
364409
return Err("never inline hint");
365410
}
366411

367-
// Only inline local functions if they would be eligible for cross-crate
368-
// inlining. This is to ensure that the final crate doesn't have MIR that
369-
// reference unexported symbols
370-
if callsite.callee.def_id().is_local() {
371-
let is_generic = callsite.callee.substs.non_erasable_generics().next().is_some();
372-
if !is_generic && !callee_attrs.requests_inline() {
373-
return Err("not exported");
374-
}
375-
}
376-
377412
if callsite.fn_sig.c_variadic() {
378413
return Err("C variadic");
379414
}
@@ -1148,3 +1183,41 @@ fn try_instance_mir<'tcx>(
11481183
_ => Ok(tcx.instance_mir(instance)),
11491184
}
11501185
}
1186+
1187+
fn contains_pointer_constant(body: &Body<'_>) -> bool {
1188+
let mut finder = ConstFinder { found: false };
1189+
finder.visit_body(body);
1190+
finder.found
1191+
}
1192+
1193+
struct ConstFinder {
1194+
found: bool,
1195+
}
1196+
1197+
impl Visitor<'_> for ConstFinder {
1198+
fn visit_constant(&mut self, constant: &Constant<'_>, location: Location) {
1199+
debug!("Visiting constant: {:?}", constant.literal);
1200+
if let ConstantKind::Unevaluated(..) = constant.literal {
1201+
self.found = true;
1202+
return;
1203+
}
1204+
1205+
if let ConstantKind::Val(val, ty) = constant.literal {
1206+
ty::tls::with(|tcx| {
1207+
let val = tcx.lift(val).unwrap();
1208+
if let ConstValue::Scalar(Scalar::Ptr(ptr, _size)) = val {
1209+
if let Some(GlobalAlloc::Static(_)) =
1210+
tcx.try_get_global_alloc(ptr.into_parts().0)
1211+
{
1212+
self.found = true;
1213+
}
1214+
}
1215+
});
1216+
1217+
if ty.is_fn() {
1218+
self.found = true;
1219+
}
1220+
}
1221+
self.super_constant(constant, location);
1222+
}
1223+
}

tests/codegen-units/item-collection/items-within-generic-items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![deny(dead_code)]
44
#![feature(start)]
55

6+
#[inline(never)]
67
fn generic_fn<T>(a: T) -> (T, i32) {
78
//~ MONO_ITEM fn generic_fn::nested_fn
89
fn nested_fn(a: i32) -> i32 {

tests/codegen/call-metadata.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub fn test() {
1212
}
1313

1414
#[no_mangle]
15+
#[inline(never)]
1516
fn some_true() -> Option<bool> {
1617
Some(true)
1718
}

tests/codegen/cold-call-declare-and-call.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// CHECK: call coldcc void @this_should_never_happen(i16
1010

1111
#[no_mangle]
12+
#[inline(never)]
1213
pub extern "rust-cold" fn this_should_never_happen(x: u16) {}
1314

1415
pub fn do_things(x: u16) {

tests/codegen/drop.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ impl Drop for SomeUniqueName {
1111
}
1212
}
1313

14+
#[inline(never)]
1415
pub fn possibly_unwinding() {
1516
}
1617

tests/codegen/personality_lifetimes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ impl Drop for S {
1313
}
1414
}
1515

16+
#[inline(never)]
1617
fn might_unwind() {
1718
}
1819

tests/codegen/target-cpu-on-functions.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ pub extern "C" fn exported() {
1515

1616
// CHECK-LABEL: ; target_cpu_on_functions::not_exported
1717
// CHECK-NEXT: ; Function Attrs:
18-
// CHECK-NEXT: define {{.*}}() {{.*}} #0
18+
// CHECK-NEXT: define {{.*}}() {{.*}} #1
19+
#[inline(never)]
1920
fn not_exported() {}
2021

2122
// CHECK: attributes #0 = {{.*}} "target-cpu"="{{.*}}"

tests/codegen/tune-cpu-on-functions.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ pub extern fn exported() {
1515

1616
// CHECK-LABEL: ; tune_cpu_on_functions::not_exported
1717
// CHECK-NEXT: ; Function Attrs:
18-
// CHECK-NEXT: define {{.*}}() {{.*}} #0
18+
// CHECK-NEXT: define {{.*}}() {{.*}} #1
19+
#[inline(never)]
1920
fn not_exported() {}
2021

2122
// CHECK: attributes #0 = {{.*}} "tune-cpu"="{{.*}}"

tests/incremental/hashes/let_expressions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ pub fn add_ref_in_pattern() {
142142
}
143143

144144
#[cfg(not(any(cfail1,cfail4)))]
145-
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck,optimized_mir")]
145+
#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck,optimized_mir,promoted_mir")]
146146
#[rustc_clean(cfg="cfail3")]
147-
#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,typeck,optimized_mir")]
147+
#[rustc_clean(cfg="cfail5", except="hir_owner_nodes,typeck,optimized_mir,promoted_mir")]
148148
#[rustc_clean(cfg="cfail6")]
149149
pub fn add_ref_in_pattern() {
150150
let (ref _a, _b) = (1u8, 'y');

tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff

+4-12
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,19 @@
44
fn main() -> () {
55
let mut _0: ();
66
let _1: main::Un;
7-
let mut _2: u32;
8-
let mut _3: u32;
97
scope 1 {
108
debug un => _1;
119
scope 2 {
1210
}
13-
scope 3 (inlined std::mem::drop::<u32>) {
14-
debug _x => _3;
11+
scope 4 (inlined std::mem::drop::<u32>) {
12+
debug _x => const 1_u32;
1513
}
1614
}
15+
scope 3 (inlined val) {
16+
}
1717

1818
bb0: {
1919
StorageLive(_1);
20-
StorageLive(_2);
21-
_2 = val() -> [return: bb1, unwind unreachable];
22-
}
23-
24-
bb1: {
25-
StorageDead(_2);
26-
StorageLive(_3);
27-
StorageDead(_3);
2820
StorageDead(_1);
2921
return;
3022
}

tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff

+4-12
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,19 @@
44
fn main() -> () {
55
let mut _0: ();
66
let _1: main::Un;
7-
let mut _2: u32;
8-
let mut _3: u32;
97
scope 1 {
108
debug un => _1;
119
scope 2 {
1210
}
13-
scope 3 (inlined std::mem::drop::<u32>) {
14-
debug _x => _3;
11+
scope 4 (inlined std::mem::drop::<u32>) {
12+
debug _x => const 1_u32;
1513
}
1614
}
15+
scope 3 (inlined val) {
16+
}
1717

1818
bb0: {
1919
StorageLive(_1);
20-
StorageLive(_2);
21-
_2 = val() -> bb1;
22-
}
23-
24-
bb1: {
25-
StorageDead(_2);
26-
StorageLive(_3);
27-
StorageDead(_3);
2820
StorageDead(_1);
2921
return;
3022
}

tests/mir-opt/inline/issue_106141.outer.Inline.panic-abort.diff

+7-25
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,26 @@
44
fn outer() -> usize {
55
let mut _0: usize;
66
+ scope 1 (inlined inner) {
7-
+ let mut _1: &[bool; 1];
8-
+ let mut _2: bool;
9-
+ let mut _3: bool;
107
+ scope 2 {
118
+ debug buffer => const _;
9+
+ let _1: usize;
1210
+ scope 3 {
13-
+ debug index => _0;
11+
+ debug index => _1;
12+
+ }
13+
+ scope 4 (inlined index) {
1414
+ }
1515
+ }
1616
+ }
1717

1818
bb0: {
1919
- _0 = inner() -> [return: bb1, unwind unreachable];
2020
+ StorageLive(_1);
21-
+ _1 = const _;
22-
+ _0 = index() -> [return: bb1, unwind unreachable];
21+
+ goto -> bb1;
2322
}
2423

2524
bb1: {
26-
+ StorageLive(_3);
27-
+ _2 = Lt(_0, const 1_usize);
28-
+ assert(move _2, "index out of bounds: the length is {} but the index is {}", const 1_usize, _0) -> [success: bb2, unwind unreachable];
29-
+ }
30-
+
31-
+ bb2: {
32-
+ _3 = (*_1)[_0];
33-
+ switchInt(move _3) -> [0: bb3, otherwise: bb4];
34-
+ }
35-
+
36-
+ bb3: {
37-
+ _0 = const 0_usize;
38-
+ goto -> bb4;
39-
+ }
40-
+
41-
+ bb4: {
42-
+ StorageDead(_3);
43-
+ StorageDead(_1);
44-
return;
25+
- return;
26+
+ goto -> bb1;
4527
}
4628
}
4729

tests/mir-opt/inline/issue_106141.outer.Inline.panic-unwind.diff

+7-25
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,26 @@
44
fn outer() -> usize {
55
let mut _0: usize;
66
+ scope 1 (inlined inner) {
7-
+ let mut _1: &[bool; 1];
8-
+ let mut _2: bool;
9-
+ let mut _3: bool;
107
+ scope 2 {
118
+ debug buffer => const _;
9+
+ let _1: usize;
1210
+ scope 3 {
13-
+ debug index => _0;
11+
+ debug index => _1;
12+
+ }
13+
+ scope 4 (inlined index) {
1414
+ }
1515
+ }
1616
+ }
1717

1818
bb0: {
1919
- _0 = inner() -> bb1;
2020
+ StorageLive(_1);
21-
+ _1 = const _;
22-
+ _0 = index() -> bb1;
21+
+ goto -> bb1;
2322
}
2423

2524
bb1: {
26-
+ StorageLive(_3);
27-
+ _2 = Lt(_0, const 1_usize);
28-
+ assert(move _2, "index out of bounds: the length is {} but the index is {}", const 1_usize, _0) -> bb2;
29-
+ }
30-
+
31-
+ bb2: {
32-
+ _3 = (*_1)[_0];
33-
+ switchInt(move _3) -> [0: bb3, otherwise: bb4];
34-
+ }
35-
+
36-
+ bb3: {
37-
+ _0 = const 0_usize;
38-
+ goto -> bb4;
39-
+ }
40-
+
41-
+ bb4: {
42-
+ StorageDead(_3);
43-
+ StorageDead(_1);
44-
return;
25+
- return;
26+
+ goto -> bb1;
4527
}
4628
}
4729

0 commit comments

Comments
 (0)