@@ -308,10 +308,29 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
308308 }
309309}
310310
311+ pub ( crate ) enum FunctionSignature < ' ll > {
312+ /// The signature is obtained directly from LLVM, and **may not match the Rust signature**
313+ Intrinsic ( llvm:: Intrinsic , & ' ll Type ) ,
314+ /// The name starts with `llvm.`, but can't obtain the intrinsic ID. May be invalid or upgradable
315+ MaybeInvalidIntrinsic ( & ' ll Type ) ,
316+ /// Just the Rust signature
317+ Rust ( & ' ll Type ) ,
318+ }
319+
320+ impl < ' ll > FunctionSignature < ' ll > {
321+ pub ( crate ) fn fn_ty ( & self ) -> & ' ll Type {
322+ match self {
323+ FunctionSignature :: Intrinsic ( _, fn_ty)
324+ | FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
325+ | FunctionSignature :: Rust ( fn_ty) => fn_ty,
326+ }
327+ }
328+ }
329+
311330pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
312331 fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
313332 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 ;
333+ fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > , name : & [ u8 ] ) -> FunctionSignature < ' ll > ;
315334 fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
316335 fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
317336
@@ -327,57 +346,25 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
327346 fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) ;
328347}
329348
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 ,
349+ /// checks that the Rust signature of a llvm intrinsic is correct
350+ fn verify_intrinsic_signature < ' ll , ' tcx > (
351+ cx : & CodegenCx < ' ll , ' tcx > ,
352+ fn_ty : & ' ll Type ,
334353 rust_return_ty : & ' ll Type ,
335354 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-
355+ rust_is_variadic : bool ,
356+ ) -> bool {
354357 let llvm_return_ty = cx. get_return_type ( fn_ty) ;
355358 let llvm_argument_tys = cx. func_params_types ( fn_ty) ;
359+ let llvm_is_variadic = cx. func_is_variadic ( fn_ty) ;
356360
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- ) ;
361+ if rust_is_variadic != llvm_is_variadic || rust_argument_tys. len ( ) != llvm_argument_tys. len ( ) {
362+ return false ;
363363 }
364364
365- if !cx. equate_ty ( 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- iter:: zip ( rust_argument_tys, llvm_argument_tys) . enumerate ( )
372- {
373- if !cx. equate_ty ( 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
365+ iter:: once ( ( & rust_return_ty, & llvm_return_ty) )
366+ . chain ( iter:: zip ( rust_argument_tys, & llvm_argument_tys) )
367+ . all ( |( rust_ty, llvm_ty) | cx. equate_ty ( rust_ty, llvm_ty) )
381368}
382369
383370impl < ' ll , CX : Borrow < SCx < ' ll > > > GenericCx < ' ll , CX > {
@@ -407,19 +394,12 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
407394 vector_size_bits == 8192
408395 }
409396 TypeKind :: BFloat => rust_ty == self . type_i16 ( ) ,
410- TypeKind :: Vector if self . type_kind ( rust_ty ) == TypeKind :: Vector => {
397+ TypeKind :: Vector => {
411398 let llvm_element_count = self . vector_length ( llvm_ty) ;
412- let rust_element_count = self . vector_length ( rust_ty) ;
413-
414- if llvm_element_count != rust_element_count {
415- return false ;
416- }
417-
418399 let llvm_element_ty = self . element_type ( llvm_ty) ;
419- let rust_element_ty = self . element_type ( rust_ty) ;
420400
421401 if llvm_element_ty == self . type_bf16 ( ) {
422- rust_element_ty == self . type_i16 ( )
402+ rust_ty == self . type_vector ( self . type_i16 ( ) , llvm_element_count as u64 )
423403 } else {
424404 false
425405 }
@@ -502,41 +482,62 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
502482 llargument_tys
503483 }
504484
505- fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > , name : & [ u8 ] ) -> & ' ll Type {
485+ fn llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > , name : & [ u8 ] ) -> FunctionSignature < ' ll > {
506486 let return_ty = self . llvm_return_type ( cx) ;
507487 let argument_tys = self . llvm_argument_types ( cx) ;
508488
489+ let mut maybe_invalid = false ;
490+
509491 if name. starts_with ( b"llvm." ) {
510- if let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name)
511- && !intrinsic. is_overloaded ( )
512- {
492+ if let Some ( intrinsic) = llvm:: Intrinsic :: lookup ( name) {
513493 if !intrinsic. is_overloaded ( ) {
514494 // FIXME: also do this for overloaded intrinsics
515- return match_intrinsic_signature (
495+ let llvm_fn_ty = cx. intrinsic_type ( intrinsic, & [ ] ) ;
496+ if verify_intrinsic_signature (
516497 cx,
517- intrinsic ,
498+ llvm_fn_ty ,
518499 return_ty,
519500 & argument_tys,
520- name,
521- ) ;
501+ self . c_variadic ,
502+ ) {
503+ return FunctionSignature :: Intrinsic ( intrinsic, llvm_fn_ty) ;
504+ } else {
505+ // FIXME: ideally we would like to hard error, but there are edge cases like `i1xN`
506+ // and differences between struct representations that we have to tackle first
507+
508+ let rust_fn_ty = if self . c_variadic {
509+ cx. type_variadic_func ( & argument_tys, return_ty)
510+ } else {
511+ cx. type_func ( & argument_tys, return_ty)
512+ } ;
513+ cx. tcx . dcx ( ) . note ( format ! (
514+ "Intrinsic signature mismatch for `{}`: expected signature `{llvm_fn_ty:?}`, found signature `{rust_fn_ty:?}`" ,
515+ str :: from_utf8( name) . unwrap( )
516+ ) ) ;
517+ // fallback to the rust signature
518+ return FunctionSignature :: Rust ( rust_fn_ty) ;
519+ }
522520 }
523521 } else {
524522 // it's one of 2 cases,
525523 // - either the base name is invalid
526524 // - it has been superceded by something else, so the intrinsic was removed entirely
527- //
528- // anyway, let's log it
529- tracing:: debug!(
530- "Couldn't find intrinsic `{}`, either invalid or deprecated" ,
531- str :: from_utf8( name) . unwrap( )
532- ) ;
525+ // to check for upgrades, we need the `llfn`, so we defer it for now
526+
527+ maybe_invalid = true ;
533528 }
534529 }
535530
536- if self . c_variadic {
531+ let fn_ty = if self . c_variadic {
537532 cx. type_variadic_func ( & argument_tys, return_ty)
538533 } else {
539534 cx. type_func ( & argument_tys, return_ty)
535+ } ;
536+
537+ if maybe_invalid {
538+ FunctionSignature :: MaybeInvalidIntrinsic ( fn_ty)
539+ } else {
540+ FunctionSignature :: Rust ( fn_ty)
540541 }
541542 }
542543
0 commit comments