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