@@ -146,6 +146,12 @@ impl<'tcx> Inliner<'tcx> {
146
146
debug ! ( "inlined {}" , callsite. callee) ;
147
147
self . changed = true ;
148
148
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
+
149
155
self . history . push ( callsite. callee . def_id ( ) ) ;
150
156
self . process_blocks ( caller_body, new_blocks) ;
151
157
self . history . pop ( ) ;
@@ -184,6 +190,7 @@ impl<'tcx> Inliner<'tcx> {
184
190
185
191
self . check_mir_is_available ( caller_body, & callsite. callee ) ?;
186
192
let callee_body = try_instance_mir ( self . tcx , callsite. callee . def ) ?;
193
+ self . check_mir_is_exportable ( & callsite. callee , callee_attrs, callee_body) ?;
187
194
self . check_mir_body ( callsite, callee_body, callee_attrs) ?;
188
195
189
196
if !self . tcx . consider_optimizing ( || {
@@ -353,6 +360,44 @@ impl<'tcx> Inliner<'tcx> {
353
360
None
354
361
}
355
362
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
+
356
401
/// Returns an error if inlining is not possible based on codegen attributes alone. A success
357
402
/// indicates that inlining decision should be based on other criteria.
358
403
fn check_codegen_attributes (
@@ -364,16 +409,6 @@ impl<'tcx> Inliner<'tcx> {
364
409
return Err ( "never inline hint" ) ;
365
410
}
366
411
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
-
377
412
if callsite. fn_sig . c_variadic ( ) {
378
413
return Err ( "C variadic" ) ;
379
414
}
@@ -1148,3 +1183,41 @@ fn try_instance_mir<'tcx>(
1148
1183
_ => Ok ( tcx. instance_mir ( instance) ) ,
1149
1184
}
1150
1185
}
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
+ }
0 commit comments