@@ -27,7 +27,7 @@ use rustc_codegen_ssa::traits::{
2727} ;
2828use rustc_middle:: bug;
2929use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrs ;
30- use rustc_middle:: ty:: layout:: TyAndLayout ;
30+ use rustc_middle:: ty:: layout:: { LayoutOf , TyAndLayout } ;
3131use rustc_middle:: ty:: { self , AtomicOrdering , Ty } ;
3232use rustc_span:: Span ;
3333use rustc_target:: callconv:: FnAbi ;
@@ -1721,30 +1721,15 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
17211721 fn checked_binop (
17221722 & mut self ,
17231723 oop : OverflowOp ,
1724- ty : Ty < ' _ > ,
1724+ ty : Ty < ' tcx > ,
17251725 lhs : Self :: Value ,
17261726 rhs : Self :: Value ,
17271727 ) -> ( Self :: Value , Self :: Value ) {
1728- // adopted partially from https://github.com/ziglang/zig/blob/master/src/codegen/spirv.zig
1729- let is_add = match oop {
1730- OverflowOp :: Add => true ,
1731- OverflowOp :: Sub => false ,
1732- OverflowOp :: Mul => {
1733- // NOTE(eddyb) this needs to be `undef`, not `false`/`true`, because
1734- // we don't want the user's boolean constants to keep the zombie alive.
1735- let bool = SpirvType :: Bool . def ( self . span ( ) , self ) ;
1736- let overflowed = self . undef ( bool) ;
1737-
1738- let result = ( self . mul ( lhs, rhs) , overflowed) ;
1739- self . zombie ( result. 1 . def ( self ) , "checked mul is not supported yet" ) ;
1740- return result;
1741- }
1742- } ;
17431728 let signed = match ty. kind ( ) {
17441729 ty:: Int ( _) => true ,
17451730 ty:: Uint ( _) => false ,
1746- other => self . fatal ( format ! (
1747- "Unexpected {} type: {other:#? }" ,
1731+ _ => self . fatal ( format ! (
1732+ "unexpected {} type: {ty }" ,
17481733 match oop {
17491734 OverflowOp :: Add => "checked add" ,
17501735 OverflowOp :: Sub => "checked sub" ,
@@ -1753,13 +1738,17 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
17531738 ) ) ,
17541739 } ;
17551740
1756- let result = if is_add {
1757- self . add ( lhs, rhs)
1758- } else {
1759- self . sub ( lhs, rhs)
1760- } ;
1741+ // HACK(eddyb) SPIR-V `OpIAddCarry`/`OpISubBorrow` are specifically for
1742+ // unsigned overflow, so signed overflow still needs this custom logic.
1743+ if signed && let OverflowOp :: Add | OverflowOp :: Sub = oop {
1744+ let result = match oop {
1745+ OverflowOp :: Add => self . add ( lhs, rhs) ,
1746+ OverflowOp :: Sub => self . sub ( lhs, rhs) ,
1747+ OverflowOp :: Mul => unreachable ! ( ) ,
1748+ } ;
1749+
1750+ // adopted partially from https://github.com/ziglang/zig/blob/master/src/codegen/spirv.zig
17611751
1762- let overflowed = if signed {
17631752 // when adding, overflow could happen if
17641753 // - rhs is positive and result < lhs; or
17651754 // - rhs is negative and result > lhs
@@ -1771,30 +1760,80 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
17711760 // this is equivalent to (rhs < 0) == (result < lhs)
17721761 let rhs_lt_zero = self . icmp ( IntPredicate :: IntSLT , rhs, self . constant_int ( rhs. ty , 0 ) ) ;
17731762 let result_gt_lhs = self . icmp (
1774- if is_add {
1775- IntPredicate :: IntSGT
1776- } else {
1777- IntPredicate :: IntSLT
1763+ match oop {
1764+ OverflowOp :: Add => IntPredicate :: IntSGT ,
1765+ OverflowOp :: Sub => IntPredicate :: IntSLT ,
1766+ OverflowOp :: Mul => unreachable ! ( ) ,
17781767 } ,
17791768 result,
17801769 lhs,
17811770 ) ;
1782- self . icmp ( IntPredicate :: IntEQ , rhs_lt_zero, result_gt_lhs)
1783- } else {
1784- // for unsigned addition, overflow occurred if the result is less than any of the operands.
1785- // for subtraction, overflow occurred if the result is greater.
1786- self . icmp (
1787- if is_add {
1788- IntPredicate :: IntULT
1771+
1772+ let overflowed = self . icmp ( IntPredicate :: IntEQ , rhs_lt_zero, result_gt_lhs) ;
1773+
1774+ return ( result, overflowed) ;
1775+ }
1776+
1777+ let result_type = self . layout_of ( ty) . spirv_type ( self . span ( ) , self ) ;
1778+ let pair_result_type = {
1779+ let field_types = [ result_type, result_type] ;
1780+ let ( field_offsets, size, align) = crate :: abi:: auto_struct_layout ( self , & field_types) ;
1781+ SpirvType :: Adt {
1782+ def_id : None ,
1783+ size,
1784+ align,
1785+ field_types : & field_types,
1786+ field_offsets : & field_offsets,
1787+ field_names : None ,
1788+ }
1789+ . def ( self . span ( ) , self )
1790+ } ;
1791+
1792+ let lhs = lhs. def ( self ) ;
1793+ let rhs = rhs. def ( self ) ;
1794+ let pair_result = match oop {
1795+ OverflowOp :: Add => self
1796+ . emit ( )
1797+ . i_add_carry ( pair_result_type, None , lhs, rhs)
1798+ . unwrap ( ) ,
1799+ OverflowOp :: Sub => self
1800+ . emit ( )
1801+ . i_sub_borrow ( pair_result_type, None , lhs, rhs)
1802+ . unwrap ( ) ,
1803+ OverflowOp :: Mul => {
1804+ if signed {
1805+ self . emit ( )
1806+ . s_mul_extended ( pair_result_type, None , lhs, rhs)
1807+ . unwrap ( )
17891808 } else {
1790- IntPredicate :: IntUGT
1791- } ,
1792- result,
1793- lhs,
1794- )
1809+ self . emit ( )
1810+ . u_mul_extended ( pair_result_type, None , lhs, rhs)
1811+ . unwrap ( )
1812+ }
1813+ }
1814+ }
1815+ . with_type ( pair_result_type) ;
1816+ let result_lo = self . extract_value ( pair_result, 0 ) ;
1817+ let result_hi = self . extract_value ( pair_result, 1 ) ;
1818+
1819+ // HACK(eddyb) SPIR-V lacks any `(T, T) -> (T, bool)` instructions,
1820+ // so instead `result_hi` is compared with the value expected in the
1821+ // non-overflow case (`0`, or `-1` for negative signed multiply result).
1822+ let expected_nonoverflowing_hi = match ( oop, signed) {
1823+ ( OverflowOp :: Add | OverflowOp :: Sub , _) | ( OverflowOp :: Mul , false ) => {
1824+ self . const_uint ( result_type, 0 )
1825+ }
1826+ ( OverflowOp :: Mul , true ) => {
1827+ // HACK(eddyb) `(x: iN) >> (N - 1)` will spread the sign bit
1828+ // across all `N` bits of `iN`, and should be equivalent to
1829+ // `if x < 0 { -1 } else { 0 }`, without needing compare+select).
1830+ let result_width = u32:: try_from ( self . int_width ( result_type) ) . unwrap ( ) ;
1831+ self . ashr ( result_lo, self . const_u32 ( result_width - 1 ) )
1832+ }
17951833 } ;
1834+ let overflowed = self . icmp ( IntPredicate :: IntNE , result_hi, expected_nonoverflowing_hi) ;
17961835
1797- ( result , overflowed)
1836+ ( result_lo , overflowed)
17981837 }
17991838
18001839 // rustc has the concept of an immediate vs. memory type - bools are compiled to LLVM bools as
0 commit comments