1
- use std:: cmp;
1
+ use std:: borrow:: Borrow ;
2
+ use std:: { cmp, iter} ;
2
3
3
4
use libc:: c_uint;
4
5
use rustc_abi:: {
@@ -303,8 +304,37 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
303
304
}
304
305
}
305
306
307
+ pub ( crate ) enum FunctionSignature < ' ll > {
308
+ /// The signature is obtained directly from LLVM, and **may not match the Rust signature**
309
+ Intrinsic ( & ' ll Type ) ,
310
+ /// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
311
+ MaybeInvalidIntrinsic ( & ' ll Type ) ,
312
+ /// Just the Rust signature
313
+ Rust ( & ' ll Type ) ,
314
+ }
315
+
316
+ impl < ' ll > FunctionSignature < ' ll > {
317
+ pub ( crate ) fn fn_ty ( & self ) -> & ' ll Type {
318
+ match self {
319
+ FunctionSignature :: Intrinsic ( fn_ty)
320
+ | FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
321
+ | FunctionSignature :: Rust ( fn_ty) => fn_ty,
322
+ }
323
+ }
324
+ }
325
+
306
326
pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
307
- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
327
+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
328
+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > ;
329
+ fn llvm_type (
330
+ & self ,
331
+ cx : & CodegenCx < ' ll , ' tcx > ,
332
+ name : & [ u8 ] ,
333
+ do_verify : bool ,
334
+ ) -> FunctionSignature < ' ll > ;
335
+ /// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
336
+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_ty : & ' ll Type ) -> bool ;
337
+
308
338
fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
309
339
fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
310
340
@@ -317,30 +347,39 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
317
347
) ;
318
348
319
349
/// Apply attributes to a function call.
320
- fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) ;
350
+ fn apply_attrs_callsite (
351
+ & self ,
352
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
353
+ callsite : & ' ll Value ,
354
+ llfn : & ' ll Value ,
355
+ ) ;
321
356
}
322
357
323
358
impl < ' ll , ' tcx > FnAbiLlvmExt < ' ll , ' tcx > for FnAbi < ' tcx , Ty < ' tcx > > {
324
- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
359
+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
360
+ match & self . ret . mode {
361
+ PassMode :: Ignore => cx. type_void ( ) ,
362
+ PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
363
+ PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
364
+ PassMode :: Indirect { .. } => cx. type_void ( ) ,
365
+ }
366
+ }
367
+
368
+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > {
369
+ let indirect_return = matches ! ( self . ret. mode, PassMode :: Indirect { .. } ) ;
370
+
325
371
// Ignore "extra" args from the call site for C variadic functions.
326
372
// Only the "fixed" args are part of the LLVM function signature.
327
373
let args =
328
374
if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
329
375
330
376
// This capacity calculation is approximate.
331
- let mut llargument_tys = Vec :: with_capacity (
332
- self . args . len ( ) + if let PassMode :: Indirect { .. } = self . ret . mode { 1 } else { 0 } ,
333
- ) ;
377
+ let mut llargument_tys =
378
+ Vec :: with_capacity ( args. len ( ) + if indirect_return { 1 } else { 0 } ) ;
334
379
335
- let llreturn_ty = match & self . ret . mode {
336
- PassMode :: Ignore => cx. type_void ( ) ,
337
- PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
338
- PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
339
- PassMode :: Indirect { .. } => {
340
- llargument_tys. push ( cx. type_ptr ( ) ) ;
341
- cx. type_void ( )
342
- }
343
- } ;
380
+ if indirect_return {
381
+ llargument_tys. push ( cx. type_ptr ( ) ) ;
382
+ }
344
383
345
384
for arg in args {
346
385
// Note that the exact number of arguments pushed here is carefully synchronized with
@@ -387,10 +426,72 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
387
426
llargument_tys. push ( llarg_ty) ;
388
427
}
389
428
390
- if self . c_variadic {
391
- cx. type_variadic_func ( & llargument_tys, llreturn_ty)
429
+ llargument_tys
430
+ }
431
+
432
+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_fn_ty : & ' ll Type ) -> bool {
433
+ let rust_return_ty = self . llvm_return_type ( cx) ;
434
+ let rust_argument_tys = self . llvm_argument_types ( cx) ;
435
+
436
+ let llvm_return_ty = cx. get_return_type ( llvm_fn_ty) ;
437
+ let llvm_argument_tys = cx. func_params_types ( llvm_fn_ty) ;
438
+ let llvm_is_variadic = cx. func_is_variadic ( llvm_fn_ty) ;
439
+
440
+ if self . c_variadic != llvm_is_variadic || rust_argument_tys. len ( ) != llvm_argument_tys. len ( )
441
+ {
442
+ return false ;
443
+ }
444
+
445
+ iter:: once ( ( rust_return_ty, llvm_return_ty) )
446
+ . chain ( iter:: zip ( rust_argument_tys, llvm_argument_tys) )
447
+ . all ( |( rust_ty, llvm_ty) | rust_ty == llvm_ty)
448
+ }
449
+
450
+ fn llvm_type (
451
+ & self ,
452
+ cx : & CodegenCx < ' ll , ' tcx > ,
453
+ name : & [ u8 ] ,
454
+ do_verify : bool ,
455
+ ) -> FunctionSignature < ' ll > {
456
+ let mut maybe_invalid = false ;
457
+
458
+ if name. starts_with ( b"llvm." ) {
459
+ if let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name) {
460
+ if !intrinsic. is_overloaded ( ) {
461
+ // FIXME: also do this for overloaded intrinsics
462
+ let llvm_fn_ty = intrinsic. get_type ( cx. llcx , & [ ] ) ;
463
+ if do_verify {
464
+ if !self . verify_intrinsic_signature ( cx, llvm_fn_ty) {
465
+ cx. tcx . dcx ( ) . fatal ( format ! (
466
+ "Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`" ,
467
+ str :: from_utf8( name) . unwrap( )
468
+ ) ) ;
469
+ }
470
+ }
471
+ return FunctionSignature :: Intrinsic ( llvm_fn_ty) ;
472
+ }
473
+ } else {
474
+ // it's one of 2 cases,
475
+ // - either the base name is invalid
476
+ // - it has been superseded by something else, so the intrinsic was removed entirely
477
+ // to check for upgrades, we need the `llfn`, so we defer it for now
478
+ maybe_invalid = true ;
479
+ }
480
+ }
481
+
482
+ let return_ty = self . llvm_return_type ( cx) ;
483
+ let argument_tys = self . llvm_argument_types ( cx) ;
484
+
485
+ let fn_ty = if self . c_variadic {
486
+ cx. type_variadic_func ( & argument_tys, return_ty)
392
487
} else {
393
- cx. type_func ( & llargument_tys, llreturn_ty)
488
+ cx. type_func ( & argument_tys, return_ty)
489
+ } ;
490
+
491
+ if maybe_invalid {
492
+ FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
493
+ } else {
494
+ FunctionSignature :: Rust ( fn_ty)
394
495
}
395
496
}
396
497
@@ -548,7 +649,23 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
548
649
}
549
650
}
550
651
551
- fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) {
652
+ fn apply_attrs_callsite (
653
+ & self ,
654
+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
655
+ callsite : & ' ll Value ,
656
+ llfn : & ' ll Value ,
657
+ ) {
658
+ // if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
659
+ let name = llvm:: get_value_name ( llfn) ;
660
+ if name. starts_with ( b"llvm." )
661
+ && let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( & name)
662
+ {
663
+ // FIXME: also do this for overloaded intrinsics
664
+ if !intrinsic. is_overloaded ( ) {
665
+ return ;
666
+ }
667
+ }
668
+
552
669
let mut func_attrs = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
553
670
if self . ret . layout . is_uninhabited ( ) {
554
671
func_attrs. push ( llvm:: AttributeKind :: NoReturn . create_attr ( bx. cx . llcx ) ) ;
0 commit comments