11use std:: borrow:: Borrow ;
22use std:: cmp;
33
4+ use itertools:: zip_eq;
45use libc:: c_uint;
56use rustc_abi:: { BackendRepr , HasDataLayout , Primitive , Reg , RegKind , Size } ;
67use rustc_codegen_ssa:: MemFlags ;
@@ -308,7 +309,9 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
308309}
309310
310311pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
311- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
312+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
313+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > ;
314+ fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > , name : & [ u8 ] ) -> & ' ll Type ;
312315 fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
313316 fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
314317
@@ -324,27 +327,83 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
324327 fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) ;
325328}
326329
330+ /// checks that the Rust signature of a **non-overloaded** llvm intrinsic is correct
331+ fn match_intrinsic_signature < ' ll > (
332+ cx : & CodegenCx < ' ll , ' _ > ,
333+ intrinsic : llvm:: Intrinsic ,
334+ rust_return_ty : & ' ll Type ,
335+ rust_argument_tys : & [ & ' ll Type ] ,
336+ name : & [ u8 ] ,
337+ ) -> & ' ll Type {
338+ macro_rules! error {
339+ ( $( $t: tt) * ) => {
340+ cx. tcx. dcx( ) . fatal( format!( $( $t) * , fn_name=str :: from_utf8( name) . unwrap( ) ) )
341+ } ;
342+ }
343+
344+ let base_name = intrinsic. base_name ( ) ;
345+ if name != base_name {
346+ error ! (
347+ "Unsupported overload `{fn_name}` for non-overloaded intrinsic `{}`" ,
348+ str :: from_utf8( base_name) . unwrap( )
349+ ) ;
350+ }
351+
352+ let fn_ty = cx. intrinsic_type ( intrinsic, & [ ] ) ;
353+
354+ let llvm_return_ty = cx. get_return_type ( fn_ty) ;
355+ let llvm_argument_tys = cx. func_params_types ( fn_ty) ;
356+
357+ if rust_argument_tys. len ( ) != llvm_argument_tys. len ( ) {
358+ error ! (
359+ "Intrinsic signature mismatch: expected {} arguments for `{fn_name}`, found {} arguments" ,
360+ llvm_argument_tys. len( ) ,
361+ rust_argument_tys. len( )
362+ ) ;
363+ }
364+
365+ if rust_return_ty != llvm_return_ty {
366+ error ! (
367+ "Intrinsic signature mismatch: could not match `{rust_return_ty:?}` (found) with {llvm_return_ty:?} (expected) as return type for `{fn_name}`"
368+ ) ;
369+ }
370+ for ( idx, ( & rust_argument_ty, llvm_argument_ty) ) in
371+ zip_eq ( rust_argument_tys, llvm_argument_tys) . enumerate ( )
372+ {
373+ if rust_argument_ty != llvm_argument_ty {
374+ error ! (
375+ "Intrinsic signature mismatch: could not match `{rust_return_ty:?}` (found) with {llvm_return_ty:?} (expected) as argument {idx} for `{fn_name}`"
376+ ) ;
377+ }
378+ }
379+
380+ fn_ty
381+ }
382+
327383impl < ' ll , ' tcx > FnAbiLlvmExt < ' ll , ' tcx > for FnAbi < ' tcx , Ty < ' tcx > > {
328- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
384+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
385+ match & self . ret . mode {
386+ PassMode :: Ignore => cx. type_void ( ) ,
387+ PassMode :: Direct ( _) | PassMode :: Pair ( ..) => self . ret . layout . immediate_llvm_type ( cx) ,
388+ PassMode :: Cast { cast, pad_i32 : _ } => cast. llvm_type ( cx) ,
389+ PassMode :: Indirect { .. } => cx. type_void ( ) ,
390+ }
391+ }
392+
393+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > {
394+ let indirect_return = matches ! ( self . ret. mode, PassMode :: Indirect { .. } ) ;
395+
329396 // Ignore "extra" args from the call site for C variadic functions.
330397 // Only the "fixed" args are part of the LLVM function signature.
331398 let args =
332399 if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
333400
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- ) ;
401+ let mut llargument_tys =
402+ Vec :: with_capacity ( args. len ( ) + if indirect_return { 1 } else { 0 } ) ;
338403
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- } ;
404+ if indirect_return {
405+ llargument_tys. push ( cx. type_ptr ( ) ) ;
406+ }
348407
349408 for arg in args {
350409 // Note that the exact number of arguments pushed here is carefully synchronized with
@@ -391,10 +450,44 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391450 llargument_tys. push ( llarg_ty) ;
392451 }
393452
453+ llargument_tys
454+ }
455+
456+ fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > , name : & [ u8 ] ) -> & ' ll Type {
457+ let return_ty = self . llvm_return_type ( cx) ;
458+ let argument_tys = self . llvm_argument_types ( cx) ;
459+
460+ if name. starts_with ( b"llvm." ) {
461+ if let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name)
462+ && !intrinsic. is_overloaded ( )
463+ {
464+ if !intrinsic. is_overloaded ( ) {
465+ // FIXME: also do this for overloaded intrinsics
466+ return match_intrinsic_signature (
467+ cx,
468+ intrinsic,
469+ return_ty,
470+ & argument_tys,
471+ name,
472+ ) ;
473+ }
474+ } else {
475+ // it's one of 2 cases,
476+ // - either the base name is invalid
477+ // - it has been superceded by something else, so the intrinsic was removed entirely
478+ //
479+ // anyway, let's log it
480+ tracing:: warn!(
481+ "Couldn't find intrinsic `{}`, either invalid or deprecated" ,
482+ str :: from_utf8( name) . unwrap( )
483+ ) ;
484+ }
485+ }
486+
394487 if self . c_variadic {
395- cx. type_variadic_func ( & llargument_tys , llreturn_ty )
488+ cx. type_variadic_func ( & argument_tys , return_ty )
396489 } else {
397- cx. type_func ( & llargument_tys , llreturn_ty )
490+ cx. type_func ( & argument_tys , return_ty )
398491 }
399492 }
400493
0 commit comments