1- use std:: cmp;
1+ use std:: borrow:: Borrow ;
2+ use std:: { cmp, iter} ;
23
34use libc:: c_uint;
45use rustc_abi:: {
@@ -301,8 +302,37 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
301302 }
302303}
303304
305+ pub ( crate ) enum FunctionSignature < ' ll > {
306+ /// The signature is obtained directly from LLVM, and **may not match the Rust signature**
307+ Intrinsic ( & ' ll Type ) ,
308+ /// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
309+ MaybeInvalidIntrinsic ( & ' ll Type ) ,
310+ /// Just the Rust signature
311+ Rust ( & ' ll Type ) ,
312+ }
313+
314+ impl < ' ll > FunctionSignature < ' ll > {
315+ pub ( crate ) fn fn_ty ( & self ) -> & ' ll Type {
316+ match self {
317+ FunctionSignature :: Intrinsic ( fn_ty)
318+ | FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
319+ | FunctionSignature :: Rust ( fn_ty) => fn_ty,
320+ }
321+ }
322+ }
323+
304324pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
305- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
325+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
326+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > ;
327+ fn llvm_type (
328+ & self ,
329+ cx : & CodegenCx < ' ll , ' tcx > ,
330+ name : & [ u8 ] ,
331+ do_verify : bool ,
332+ ) -> FunctionSignature < ' ll > ;
333+ /// **If this function is an LLVM intrinsic** checks if the LLVM signature provided matches with this
334+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_ty : & ' ll Type ) -> bool ;
335+
306336 fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
307337 fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
308338
@@ -315,30 +345,39 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
315345 ) ;
316346
317347 /// Apply attributes to a function call.
318- 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+ ) ;
319354}
320355
321356impl < ' ll , ' tcx > FnAbiLlvmExt < ' ll , ' tcx > for FnAbi < ' tcx , Ty < ' tcx > > {
322- 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+
323369 // Ignore "extra" args from the call site for C variadic functions.
324370 // Only the "fixed" args are part of the LLVM function signature.
325371 let args =
326372 if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
327373
328374 // This capacity calculation is approximate.
329- let mut llargument_tys = Vec :: with_capacity (
330- self . args . len ( ) + if let PassMode :: Indirect { .. } = self . ret . mode { 1 } else { 0 } ,
331- ) ;
375+ let mut llargument_tys =
376+ Vec :: with_capacity ( args. len ( ) + if indirect_return { 1 } else { 0 } ) ;
332377
333- let llreturn_ty = match & self . ret . mode {
334- PassMode :: Ignore => cx. type_void ( ) ,
335- PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
336- PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
337- PassMode :: Indirect { .. } => {
338- llargument_tys. push ( cx. type_ptr ( ) ) ;
339- cx. type_void ( )
340- }
341- } ;
378+ if indirect_return {
379+ llargument_tys. push ( cx. type_ptr ( ) ) ;
380+ }
342381
343382 for arg in args {
344383 // Note that the exact number of arguments pushed here is carefully synchronized with
@@ -385,10 +424,72 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
385424 llargument_tys. push ( llarg_ty) ;
386425 }
387426
388- if self . c_variadic {
389- cx. type_variadic_func ( & llargument_tys, llreturn_ty)
427+ llargument_tys
428+ }
429+
430+ fn verify_intrinsic_signature ( & self , cx : & CodegenCx < ' ll , ' tcx > , llvm_fn_ty : & ' ll Type ) -> bool {
431+ let rust_return_ty = self . llvm_return_type ( cx) ;
432+ let rust_argument_tys = self . llvm_argument_types ( cx) ;
433+
434+ let llvm_return_ty = cx. get_return_type ( llvm_fn_ty) ;
435+ let llvm_argument_tys = cx. func_params_types ( llvm_fn_ty) ;
436+ let llvm_is_variadic = cx. func_is_variadic ( llvm_fn_ty) ;
437+
438+ if self . c_variadic != llvm_is_variadic || rust_argument_tys. len ( ) != llvm_argument_tys. len ( )
439+ {
440+ return false ;
441+ }
442+
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 = intrinsic. get_type ( cx. llcx , & [ ] ) ;
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 superseded 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+ maybe_invalid = true ;
477+ }
478+ }
479+
480+ let return_ty = self . llvm_return_type ( cx) ;
481+ let argument_tys = self . llvm_argument_types ( cx) ;
482+
483+ let fn_ty = if self . c_variadic {
484+ cx. type_variadic_func ( & argument_tys, return_ty)
390485 } else {
391- cx. type_func ( & llargument_tys, llreturn_ty)
486+ cx. type_func ( & argument_tys, return_ty)
487+ } ;
488+
489+ if maybe_invalid {
490+ FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
491+ } else {
492+ FunctionSignature :: Rust ( fn_ty)
392493 }
393494 }
394495
@@ -546,7 +647,23 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
546647 }
547648 }
548649
549- 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+
550667 let mut func_attrs = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
551668 if self . ret . layout . is_uninhabited ( ) {
552669 func_attrs. push ( llvm:: AttributeKind :: NoReturn . create_attr ( bx. cx . llcx ) ) ;
0 commit comments