11use std:: borrow:: Borrow ;
2- use std:: cmp;
2+ use std:: { cmp, iter } ;
33
44use libc:: c_uint;
55use rustc_abi:: {
@@ -306,8 +306,39 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
306306 }
307307}
308308
309+ pub ( crate ) enum FunctionSignature < ' ll > {
310+ /// The signature is obtained directly from LLVM, and **may not match the Rust signature**
311+ Intrinsic ( & ' ll Type ) ,
312+ /// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
313+ MaybeInvalidIntrinsic ( & ' ll Type ) ,
314+ /// Just the Rust signature
315+ Rust ( & ' ll Type ) ,
316+ }
317+
318+ impl < ' ll > FunctionSignature < ' ll > {
319+ pub ( crate ) fn fn_ty ( & self ) -> & ' ll Type {
320+ match self {
321+ FunctionSignature :: Intrinsic ( fn_ty)
322+ | FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
323+ | FunctionSignature :: Rust ( fn_ty) => fn_ty,
324+ }
325+ }
326+ }
327+
309328pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
310- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
329+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
330+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > ;
331+ /// When `do_verify` is set, this function performs checks for the signature of LLVM intrinsics
332+ /// and emits a fatal error if it doesn't match. These checks are important,but somewhat expensive
333+ /// So they are only used at function definitions, not at callsites
334+ fn llvm_type (
335+ & self ,
336+ cx : & CodegenCx < ' ll , ' tcx > ,
337+ name : & [ u8 ] ,
338+ do_verify : bool ,
339+ ) -> FunctionSignature < ' ll > ;
340+ /// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
341+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_ty : & ' ll Type ) -> bool ;
311342 fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
312343 fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
313344
@@ -320,30 +351,38 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
320351 ) ;
321352
322353 /// Apply attributes to a function call.
323- 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+ ) ;
324360}
325361
326362impl < ' ll , ' tcx > FnAbiLlvmExt < ' ll , ' tcx > for FnAbi < ' tcx , Ty < ' tcx > > {
327- 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+
328375 // Ignore "extra" args from the call site for C variadic functions.
329376 // Only the "fixed" args are part of the LLVM function signature.
330377 let args =
331378 if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
332379
333- // This capacity calculation is approximate.
334- let mut llargument_tys = Vec :: with_capacity (
335- self . args . len ( ) + if let PassMode :: Indirect { .. } = self . ret . mode { 1 } else { 0 } ,
336- ) ;
380+ let mut llargument_tys =
381+ Vec :: with_capacity ( args. len ( ) + if indirect_return { 1 } else { 0 } ) ;
337382
338- let llreturn_ty = match & self . ret . mode {
339- PassMode :: Ignore => cx. type_void ( ) ,
340- PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
341- PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
342- PassMode :: Indirect { .. } => {
343- llargument_tys. push ( cx. type_ptr ( ) ) ;
344- cx. type_void ( )
345- }
346- } ;
383+ if indirect_return {
384+ llargument_tys. push ( cx. type_ptr ( ) ) ;
385+ }
347386
348387 for arg in args {
349388 // Note that the exact number of arguments pushed here is carefully synchronized with
@@ -390,10 +429,74 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
390429 llargument_tys. push ( llarg_ty) ;
391430 }
392431
393- if self . c_variadic {
394- cx. type_variadic_func ( & llargument_tys, llreturn_ty)
432+ llargument_tys
433+ }
434+
435+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_fn_ty : & ' ll Type ) -> bool {
436+ let rust_return_ty = self . llvm_return_type ( cx) ;
437+ let rust_argument_tys = self . llvm_argument_types ( cx) ;
438+
439+ let llvm_return_ty = cx. get_return_type ( llvm_fn_ty) ;
440+ let llvm_argument_tys = cx. func_params_types ( llvm_fn_ty) ;
441+ let llvm_is_variadic = cx. func_is_variadic ( llvm_fn_ty) ;
442+
443+ if self . c_variadic != llvm_is_variadic || rust_argument_tys. len ( ) != llvm_argument_tys. len ( )
444+ {
445+ return false ;
446+ }
447+
448+ // todo: add bypasses for types not accessible from Rust here
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+
483+ maybe_invalid = true ;
484+ }
485+ }
486+
487+ let return_ty = self . llvm_return_type ( cx) ;
488+ let argument_tys = self . llvm_argument_types ( cx) ;
489+
490+ let fn_ty = if self . c_variadic {
491+ cx. type_variadic_func ( & argument_tys, return_ty)
395492 } else {
396- cx. type_func ( & llargument_tys, llreturn_ty)
493+ cx. type_func ( & argument_tys, return_ty)
494+ } ;
495+
496+ if maybe_invalid {
497+ FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
498+ } else {
499+ FunctionSignature :: Rust ( fn_ty)
397500 }
398501 }
399502
@@ -545,7 +648,23 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
545648 }
546649 }
547650
548- fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) {
651+ fn apply_attrs_callsite (
652+ & self ,
653+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
654+ callsite : & ' ll Value ,
655+ llfn : & ' ll Value ,
656+ ) {
657+ // if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
658+ let name = llvm:: get_value_name ( llfn) ;
659+ if name. starts_with ( b"llvm." )
660+ && let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name)
661+ {
662+ // FIXME: also do this for overloaded intrinsics
663+ if !intrinsic. is_overloaded ( ) {
664+ return ;
665+ }
666+ }
667+
549668 let mut func_attrs = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
550669 if self . ret . layout . is_uninhabited ( ) {
551670 func_attrs. push ( llvm:: AttributeKind :: NoReturn . create_attr ( bx. cx . llcx ) ) ;
0 commit comments