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