11use std:: borrow:: Borrow ;
2- use std:: cmp;
2+ use std:: { cmp, iter } ;
33
44use libc:: c_uint;
55use rustc_abi:: { BackendRepr , HasDataLayout , Primitive , Reg , RegKind , Size } ;
@@ -307,8 +307,39 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
307307 }
308308}
309309
310+ pub ( crate ) enum FunctionSignature < ' ll > {
311+ /// The signature is obtained directly from LLVM, and **may not match the Rust signature**
312+ Intrinsic ( & ' ll Type ) ,
313+ /// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
314+ MaybeInvalidIntrinsic ( & ' ll Type ) ,
315+ /// Just the Rust signature
316+ Rust ( & ' ll Type ) ,
317+ }
318+
319+ impl < ' ll > FunctionSignature < ' ll > {
320+ pub ( crate ) fn fn_ty ( & self ) -> & ' ll Type {
321+ match self {
322+ FunctionSignature :: Intrinsic ( fn_ty)
323+ | FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
324+ | FunctionSignature :: Rust ( fn_ty) => fn_ty,
325+ }
326+ }
327+ }
328+
310329pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
311- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
330+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
331+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > ;
332+ /// When `do_verify` is set, this function performs checks for the signature of LLVM intrinsics
333+ /// and emits a fatal error if it doesn't match. These checks are important,but somewhat expensive
334+ /// So they are only used at function definitions, not at callsites
335+ fn llvm_type (
336+ & self ,
337+ cx : & CodegenCx < ' ll , ' tcx > ,
338+ name : & [ u8 ] ,
339+ do_verify : bool ,
340+ ) -> FunctionSignature < ' ll > ;
341+ /// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
342+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_ty : & ' ll Type ) -> bool ;
312343 fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
313344 fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
314345
@@ -321,30 +352,38 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
321352 ) ;
322353
323354 /// Apply attributes to a function call.
324- fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) ;
355+ fn apply_attrs_callsite (
356+ & self ,
357+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
358+ callsite : & ' ll Value ,
359+ llfn : & ' ll Value ,
360+ ) ;
325361}
326362
327363impl < ' ll , ' tcx > FnAbiLlvmExt < ' ll , ' tcx > for FnAbi < ' tcx , Ty < ' tcx > > {
328- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
364+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
365+ match & self . ret . mode {
366+ PassMode :: Ignore => cx. type_void ( ) ,
367+ PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
368+ PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
369+ PassMode :: Indirect { .. } => cx. type_void ( ) ,
370+ }
371+ }
372+
373+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > {
374+ let indirect_return = matches ! ( self . ret. mode, PassMode :: Indirect { .. } ) ;
375+
329376 // Ignore "extra" args from the call site for C variadic functions.
330377 // Only the "fixed" args are part of the LLVM function signature.
331378 let args =
332379 if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
333380
334- // This capacity calculation is approximate.
335- let mut llargument_tys = Vec :: with_capacity (
336- self . args . len ( ) + if let PassMode :: Indirect { .. } = self . ret . mode { 1 } else { 0 } ,
337- ) ;
381+ let mut llargument_tys =
382+ Vec :: with_capacity ( args. len ( ) + if indirect_return { 1 } else { 0 } ) ;
338383
339- let llreturn_ty = match & self . ret . mode {
340- PassMode :: Ignore => cx. type_void ( ) ,
341- PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
342- PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
343- PassMode :: Indirect { .. } => {
344- llargument_tys. push ( cx. type_ptr ( ) ) ;
345- cx. type_void ( )
346- }
347- } ;
384+ if indirect_return {
385+ llargument_tys. push ( cx. type_ptr ( ) ) ;
386+ }
348387
349388 for arg in args {
350389 // Note that the exact number of arguments pushed here is carefully synchronized with
@@ -391,10 +430,74 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391430 llargument_tys. push ( llarg_ty) ;
392431 }
393432
394- if self . c_variadic {
395- 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+ // todo: add bypasses for types not accessible from Rust here
450+ iter:: once ( ( rust_return_ty, llvm_return_ty) )
451+ . chain ( iter:: zip ( rust_argument_tys, llvm_argument_tys) )
452+ . all ( |( rust_ty, llvm_ty) | rust_ty == llvm_ty)
453+ }
454+
455+ fn llvm_type (
456+ & self ,
457+ cx : & CodegenCx < ' ll , ' tcx > ,
458+ name : & [ u8 ] ,
459+ do_verify : bool ,
460+ ) -> FunctionSignature < ' ll > {
461+ let mut maybe_invalid = false ;
462+
463+ if name. starts_with ( b"llvm." ) {
464+ if let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name) {
465+ if !intrinsic. is_overloaded ( ) {
466+ // FIXME: also do this for overloaded intrinsics
467+ let llvm_fn_ty = cx. intrinsic_type ( intrinsic, & [ ] ) ;
468+ if do_verify {
469+ if !self . verify_intrinsic_signature ( cx, llvm_fn_ty) {
470+ cx. tcx . dcx ( ) . fatal ( format ! (
471+ "Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`" ,
472+ str :: from_utf8( name) . unwrap( )
473+ ) ) ;
474+ }
475+ }
476+ return FunctionSignature :: Intrinsic ( llvm_fn_ty) ;
477+ }
478+ } else {
479+ // it's one of 2 cases,
480+ // - either the base name is invalid
481+ // - it has been superceded by something else, so the intrinsic was removed entirely
482+ // to check for upgrades, we need the `llfn`, so we defer it for now
483+
484+ maybe_invalid = true ;
485+ }
486+ }
487+
488+ let return_ty = self . llvm_return_type ( cx) ;
489+ let argument_tys = self . llvm_argument_types ( cx) ;
490+
491+ let fn_ty = if self . c_variadic {
492+ cx. type_variadic_func ( & argument_tys, return_ty)
396493 } else {
397- cx. type_func ( & llargument_tys, llreturn_ty)
494+ cx. type_func ( & argument_tys, return_ty)
495+ } ;
496+
497+ if maybe_invalid {
498+ FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
499+ } else {
500+ FunctionSignature :: Rust ( fn_ty)
398501 }
399502 }
400503
@@ -531,7 +634,23 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
531634 }
532635 }
533636
534- fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) {
637+ fn apply_attrs_callsite (
638+ & self ,
639+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
640+ callsite : & ' ll Value ,
641+ llfn : & ' ll Value ,
642+ ) {
643+ // if we are using the LLVM signature, use the LLVM attributes otherwise it might be problematic
644+ let name = llvm:: get_value_name ( llfn) ;
645+ if name. starts_with ( b"llvm." )
646+ && let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name)
647+ {
648+ // FIXME: also do this for overloaded intrinsics
649+ if !intrinsic. is_overloaded ( ) {
650+ return ;
651+ }
652+ }
653+
535654 let mut func_attrs = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
536655 if self . ret . layout . is_uninhabited ( ) {
537656 func_attrs. push ( llvm:: AttributeKind :: NoReturn . create_attr ( bx. cx . llcx ) ) ;
0 commit comments