@@ -347,7 +347,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
347
347
| sym:: rotate_left
348
348
| sym:: rotate_right
349
349
| sym:: saturating_add
350
- | sym:: saturating_sub => {
350
+ | sym:: saturating_sub
351
+ | sym:: add_with_carry
352
+ | sym:: sub_with_carry
353
+ | sym:: mul_double
354
+ | sym:: mul_double_add
355
+ | sym:: mul_double_add2 => {
351
356
let ty = arg_tys[ 0 ] ;
352
357
match int_type_width_signed ( ty, self ) {
353
358
Some ( ( width, signed) ) => match name {
@@ -417,6 +422,76 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
417
422
) ;
418
423
self . call_intrinsic ( llvm_name, & [ lhs, rhs] )
419
424
}
425
+ sym:: add_with_carry | sym:: sub_with_carry => {
426
+ let llty = self . type_ix ( width) ;
427
+ let is_add = name == sym:: add_with_carry;
428
+ let lhs = args[ 0 ] . immediate ( ) ;
429
+ let rhs = args[ 1 ] . immediate ( ) ;
430
+
431
+ // sign-extending the carry would treat it as -1, not 1
432
+ let carry = self . intcast ( args[ 2 ] . immediate ( ) , llty, false ) ;
433
+
434
+ let llvm_name = & format ! (
435
+ "llvm.{}{}.with.overflow.i{}" ,
436
+ if signed { 's' } else { 'u' } ,
437
+ if is_add { "add" } else { "sub" } ,
438
+ width,
439
+ ) ;
440
+
441
+ let ret = self . call_intrinsic ( llvm_name, & [ lhs, rhs] ) ;
442
+ let agg = self . extract_value ( ret, 0 ) ;
443
+ let overflow1 = self . extract_value ( ret, 1 ) ;
444
+
445
+ let ret = self . call_intrinsic ( llvm_name, & [ agg, carry] ) ;
446
+ let agg = self . extract_value ( ret, 0 ) ;
447
+ let overflow2 = self . extract_value ( ret, 1 ) ;
448
+
449
+ let overflow = if signed {
450
+ self . icmp ( IntPredicate :: IntNE , overflow1, overflow2)
451
+ } else {
452
+ self . or ( overflow1, overflow2)
453
+ } ;
454
+
455
+ let holder = self . const_struct (
456
+ & [ self . const_undef ( llty) , self . const_undef ( self . type_i1 ( ) ) ] ,
457
+ false ,
458
+ ) ;
459
+ let holder = self . insert_value ( holder, agg, 0 ) ;
460
+ let holder = self . insert_value ( holder, overflow, 1 ) ;
461
+ holder
462
+ }
463
+ sym:: mul_double | sym:: mul_double_add | sym:: mul_double_add2 => {
464
+ let single_ty = self . type_ix ( width) ;
465
+ let double_ty = self . type_ix ( width * 2 ) ;
466
+ let lhs = self . intcast ( args[ 0 ] . immediate ( ) , double_ty, signed) ;
467
+ let rhs = self . intcast ( args[ 1 ] . immediate ( ) , double_ty, signed) ;
468
+ let mut ret = self . mul ( lhs, rhs) ;
469
+ if name == sym:: mul_double_add || name == sym:: mul_double_add2 {
470
+ let carry = self . intcast ( args[ 2 ] . immediate ( ) , double_ty, signed) ;
471
+ ret = self . add ( ret, carry)
472
+ }
473
+ if name == sym:: mul_double_add2 {
474
+ let carry2 = self . intcast ( args[ 3 ] . immediate ( ) , double_ty, signed) ;
475
+ ret = self . add ( ret, carry2) ;
476
+ }
477
+
478
+ // note: insignificant part is always treated as unsigned, even if we
479
+ // coerce it to signed in the final result to make the intrinsic
480
+ // signature simpler
481
+ let lo = self . intcast ( ret, single_ty, signed) ;
482
+
483
+ let bits = self . const_uint ( double_ty, width) ;
484
+ let hi = self . ashr ( ret, bits) ;
485
+ let hi = self . intcast ( hi, single_ty, signed) ;
486
+
487
+ let holder = self . const_struct (
488
+ & [ self . const_undef ( single_ty) , self . const_undef ( single_ty) ] ,
489
+ false ,
490
+ ) ;
491
+ let holder = self . insert_value ( holder, lo, 0 ) ;
492
+ let holder = self . insert_value ( holder, hi, 1 ) ;
493
+ holder
494
+ }
420
495
_ => bug ! ( ) ,
421
496
} ,
422
497
None => {
0 commit comments