@@ -184,6 +184,7 @@ impl<'tcx> Inliner<'tcx> {
184
184
185
185
self . check_mir_is_available ( caller_body, & callsite. callee ) ?;
186
186
let callee_body = try_instance_mir ( self . tcx , callsite. callee . def ) ?;
187
+ self . check_mir_is_exportable ( & callsite. callee , callee_attrs, callee_body) ?;
187
188
self . check_mir_body ( callsite, callee_body, callee_attrs) ?;
188
189
189
190
if !self . tcx . consider_optimizing ( || {
@@ -353,6 +354,44 @@ impl<'tcx> Inliner<'tcx> {
353
354
None
354
355
}
355
356
357
+ fn check_mir_is_exportable (
358
+ & self ,
359
+ callee : & Instance < ' tcx > ,
360
+ callee_attrs : & CodegenFnAttrs ,
361
+ callee_body : & Body < ' tcx > ,
362
+ ) -> Result < ( ) , & ' static str > {
363
+ if !callee. def_id ( ) . is_local ( ) {
364
+ return Ok ( ( ) ) ;
365
+ }
366
+ if self . tcx . is_constructor ( callee. def_id ( ) ) {
367
+ return Ok ( ( ) ) ;
368
+ }
369
+ if callee_attrs. requests_inline ( ) {
370
+ return Ok ( ( ) ) ;
371
+ }
372
+ let is_generic = callee. substs . non_erasable_generics ( ) . next ( ) . is_some ( ) ;
373
+ if is_generic {
374
+ return Ok ( ( ) ) ;
375
+ }
376
+
377
+ // So now we are trying to inline a function from another crate which is not inline and is
378
+ // not a generic. This might work. But if this pulls in a symbol from the other crater, we
379
+ // will fail to link.
380
+ // So this is our heuritic for handling this:
381
+ // If this function has any calls in it, that might reference an internal symbol.
382
+ // If this function contains a pointer constant, that might be a reference to an internal
383
+ // static.
384
+ // So if either of those conditions are met, we cannot inline this.
385
+ if self . tcx . mir_inliner_callees ( callee. def ) . is_empty ( )
386
+ && !contains_pointer_constant ( callee_body)
387
+ {
388
+ debug ! ( "Has no calls and no pointer constants, must be exportable" ) ;
389
+ return Ok ( ( ) ) ;
390
+ }
391
+
392
+ Err ( "not exported" )
393
+ }
394
+
356
395
/// Returns an error if inlining is not possible based on codegen attributes alone. A success
357
396
/// indicates that inlining decision should be based on other criteria.
358
397
fn check_codegen_attributes (
@@ -364,16 +403,6 @@ impl<'tcx> Inliner<'tcx> {
364
403
return Err ( "never inline hint" ) ;
365
404
}
366
405
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
406
if callsite. fn_sig . c_variadic ( ) {
378
407
return Err ( "C variadic" ) ;
379
408
}
@@ -1147,3 +1176,25 @@ fn try_instance_mir<'tcx>(
1147
1176
_ => Ok ( tcx. instance_mir ( instance) ) ,
1148
1177
}
1149
1178
}
1179
+
1180
+ fn contains_pointer_constant ( body : & Body < ' _ > ) -> bool {
1181
+ let mut finder = ConstFinder { found : false } ;
1182
+ finder. visit_body ( body) ;
1183
+ finder. found
1184
+ }
1185
+
1186
+ struct ConstFinder {
1187
+ found : bool ,
1188
+ }
1189
+
1190
+ impl Visitor < ' _ > for ConstFinder {
1191
+ fn visit_constant ( & mut self , constant : & Constant < ' _ > , location : Location ) {
1192
+ debug ! ( "Visiting constant: {:?}" , constant. literal) ;
1193
+ if let ConstantKind :: Val ( _val, ty) = constant. literal {
1194
+ if ty. is_any_ptr ( ) || ty. is_fn ( ) {
1195
+ self . found = true ;
1196
+ }
1197
+ }
1198
+ self . super_constant ( constant, location) ;
1199
+ }
1200
+ }
0 commit comments