@@ -347,7 +347,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
347347 | sym:: rotate_left
348348 | sym:: rotate_right
349349 | 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 => {
351356 let ty = arg_tys[ 0 ] ;
352357 match int_type_width_signed ( ty, self ) {
353358 Some ( ( width, signed) ) => match name {
@@ -417,6 +422,76 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
417422 ) ;
418423 self . call_intrinsic ( llvm_name, & [ lhs, rhs] )
419424 }
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+ }
420495 _ => bug ! ( ) ,
421496 } ,
422497 None => {
0 commit comments