1
1
use std:: borrow:: Borrow ;
2
2
use std:: cmp;
3
+ use std:: iter:: zip;
3
4
4
5
use libc:: c_uint;
5
6
use rustc_abi:: { BackendRepr , HasDataLayout , Primitive , Reg , RegKind , Size } ;
@@ -308,7 +309,9 @@ impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
308
309
}
309
310
310
311
pub ( 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 ;
312
315
fn ptr_to_llvm_type ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> & ' ll Type ;
313
316
fn llvm_cconv ( & self , cx : & CodegenCx < ' ll , ' tcx > ) -> llvm:: CallConv ;
314
317
@@ -325,26 +328,29 @@ pub(crate) trait FnAbiLlvmExt<'ll, 'tcx> {
325
328
}
326
329
327
330
impl < ' 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
+
329
343
// Ignore "extra" args from the call site for C variadic functions.
330
344
// Only the "fixed" args are part of the LLVM function signature.
331
345
let args =
332
346
if self . c_variadic { & self . args [ ..self . fixed_count as usize ] } else { & self . args } ;
333
347
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 } ) ;
338
350
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
+ }
348
354
349
355
for arg in args {
350
356
// 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>> {
391
397
llargument_tys. push ( llarg_ty) ;
392
398
}
393
399
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
+
394
455
if self . c_variadic {
395
- cx. type_variadic_func ( & llargument_tys , llreturn_ty )
456
+ cx. type_variadic_func ( & actual_argument_tys , actual_return_ty )
396
457
} else {
397
- cx. type_func ( & llargument_tys , llreturn_ty )
458
+ cx. type_func ( & actual_argument_tys , actual_return_ty )
398
459
}
399
460
}
400
461
0 commit comments