@@ -308,10 +308,29 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
308
308
}
309
309
}
310
310
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
+
311
330
pub ( crate ) trait FnAbiLlvmExt < ' ll , ' tcx > {
312
331
fn llvm_return_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
313
332
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 > ;
315
334
fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
316
335
fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
317
336
@@ -327,57 +346,25 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
327
346
fn apply_attrs_callsite ( & self , bx : & mut Builder < ' _ , ' ll , ' tcx > , callsite : & ' ll Value ) ;
328
347
}
329
348
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 ,
334
353
rust_return_ty : & ' ll Type ,
335
354
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 {
354
357
let llvm_return_ty = cx. get_return_type ( fn_ty) ;
355
358
let llvm_argument_tys = cx. func_params_types ( fn_ty) ;
359
+ let llvm_is_variadic = cx. func_is_variadic ( fn_ty) ;
356
360
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 ;
363
363
}
364
364
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) )
381
368
}
382
369
383
370
impl < ' ll , CX : Borrow < SCx < ' ll > > > GenericCx < ' ll , CX > {
@@ -407,19 +394,12 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
407
394
vector_size_bits == 8192
408
395
}
409
396
TypeKind :: BFloat => rust_ty == self . type_i16 ( ) ,
410
- TypeKind :: Vector if self . type_kind ( rust_ty ) == TypeKind :: Vector => {
397
+ TypeKind :: Vector => {
411
398
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
-
418
399
let llvm_element_ty = self . element_type ( llvm_ty) ;
419
- let rust_element_ty = self . element_type ( rust_ty) ;
420
400
421
401
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 )
423
403
} else {
424
404
false
425
405
}
@@ -502,41 +482,62 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
502
482
llargument_tys
503
483
}
504
484
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 > {
506
486
let return_ty = self . llvm_return_type ( cx) ;
507
487
let argument_tys = self . llvm_argument_types ( cx) ;
508
488
489
+ let mut maybe_invalid = false ;
490
+
509
491
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) {
513
493
if !intrinsic. is_overloaded ( ) {
514
494
// 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 (
516
497
cx,
517
- intrinsic ,
498
+ llvm_fn_ty ,
518
499
return_ty,
519
500
& 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
+ }
522
520
}
523
521
} else {
524
522
// it's one of 2 cases,
525
523
// - either the base name is invalid
526
524
// - 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 ;
533
528
}
534
529
}
535
530
536
- if self . c_variadic {
531
+ let fn_ty = if self . c_variadic {
537
532
cx. type_variadic_func ( & argument_tys, return_ty)
538
533
} else {
539
534
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)
540
541
}
541
542
}
542
543
0 commit comments