11use std:: borrow:: Borrow ;
22use std:: cmp;
3+ use std:: iter:: zip;
34
45use libc:: c_uint;
56use rustc_abi:: { BackendRepr , HasDataLayout , Primitive , Reg , RegKind , Size } ;
@@ -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
@@ -325,26 +328,29 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
325328}
326329
327330impl < ' ll , ' tcx > FnAbiLlvmExt < ' ll , ' tcx > for FnAbi < ' tcx , Ty < ' tcx > > {
328- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
331+ fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type {
332+ 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 { .. } => cx. type_void ( ) ,
337+ }
338+ }
339+
340+ fn llvm_argument_types ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> Vec < & ' ll Type > {
341+ let indirect_return = matches ! ( self . ret. mode, PassMode :: Indirect { .. } ) ;
342+
329343 // Ignore "extra" args from the call site for C variadic functions.
330344 // Only the "fixed" args are part of the LLVM function signature.
331345 let args =
332346 if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
333347
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- ) ;
348+ let mut llargument_tys =
349+ Vec :: with_capacity ( args. len ( ) + if indirect_return { 1 } else { 0 } ) ;
338350
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- } ;
351+ if indirect_return {
352+ llargument_tys. push ( cx. type_ptr ( ) ) ;
353+ }
348354
349355 for arg in args {
350356 // Note that the exact number of arguments pushed here is carefully synchronized with
@@ -391,10 +397,65 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
391397 llargument_tys. push ( llarg_ty) ;
392398 }
393399
400+ llargument_tys
401+ }
402+
403+ fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > , name : & [ u8 ] ) -> & ' ll Type {
404+ let actual_return_ty = self . llvm_return_type ( cx) ;
405+ let actual_argument_tys = self . llvm_argument_types ( cx) ;
406+
407+ let is_llvm_intrinsic = name. starts_with ( b"llvm." ) ;
408+
409+ if is_llvm_intrinsic && let Some ( ( _, fn_ty) ) = cx. get_intrinsic ( name) {
410+ let expected_return_ty = cx. get_return_type ( fn_ty) ;
411+ let expected_argument_tys = cx. func_params_types ( fn_ty) ;
412+
413+ if actual_argument_tys. len ( ) != expected_argument_tys. len ( ) {
414+ cx. tcx . dcx ( ) . fatal ( format ! (
415+ "Intrinsic signature mismatch: expected {} arguments for `{}`, found {} arguments" ,
416+ expected_argument_tys. len( ) ,
417+ str :: from_utf8( name) . unwrap( ) ,
418+ actual_argument_tys. len( ) ,
419+ ) ) ;
420+ }
421+
422+ if actual_return_ty != expected_return_ty {
423+ cx. tcx . dcx ( ) . fatal ( format ! (
424+ "Intrinsic signature mismatch: expected {expected_return_ty:?} as return type for `{}`, found {actual_return_ty:?}" ,
425+ str :: from_utf8( name) . unwrap( )
426+ ) ) ;
427+ }
428+ for ( idx, ( actual_argument_ty, expected_argument_ty) ) in
429+ zip ( actual_argument_tys, expected_argument_tys) . enumerate ( )
430+ {
431+ if actual_argument_ty != expected_argument_ty {
432+ cx. tcx . dcx ( ) . fatal ( format ! (
433+ "Intrinsic signature mismatch: expected {expected_argument_ty:?} as argument {idx} for `{}`, found {actual_argument_ty:?}" ,
434+ str :: from_utf8( name) . unwrap( )
435+ ) ) ;
436+ }
437+ }
438+ // todo: verify using `Intrinsic::getIntrinsicSignature`
439+
440+ return fn_ty;
441+ }
442+
443+ if is_llvm_intrinsic {
444+ // it's one of 2 cases,
445+ // - either the base name is invalid
446+ // - it has been superceded by something else, so the intrinsic was removed entirely
447+ // - our parse isn't recognizing it correctly
448+ // anyway, let's log it
449+ tracing:: debug!(
450+ "Couldn't parse intrinsic name `{}`, falling back to default implementation" ,
451+ str :: from_utf8( name) . unwrap( )
452+ ) ;
453+ }
454+
394455 if self . c_variadic {
395- cx. type_variadic_func ( & llargument_tys , llreturn_ty )
456+ cx. type_variadic_func ( & actual_argument_tys , actual_return_ty )
396457 } else {
397- cx. type_func ( & llargument_tys , llreturn_ty )
458+ cx. type_func ( & actual_argument_tys , actual_return_ty )
398459 }
399460 }
400461
0 commit comments