@@ -26,6 +26,7 @@ use rustc_codegen_ssa::traits::{
2626} ;
2727use rustc_middle:: bug;
2828use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrs ;
29+ use rustc_middle:: ty:: layout:: LayoutOf ;
2930use rustc_middle:: ty:: { self , AtomicOrdering , Ty } ;
3031use rustc_span:: Span ;
3132use rustc_target:: callconv:: FnAbi ;
@@ -1721,30 +1722,15 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
17211722 fn checked_binop (
17221723 & mut self ,
17231724 oop : OverflowOp ,
1724- ty : Ty < ' _ > ,
1725+ ty : Ty < ' tcx > ,
17251726 lhs : Self :: Value ,
17261727 rhs : Self :: Value ,
17271728 ) -> ( 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- } ;
17431729 let signed = match ty. kind ( ) {
17441730 ty:: Int ( _) => true ,
17451731 ty:: Uint ( _) => false ,
1746- other => self . fatal ( format ! (
1747- "Unexpected {} type: {other:#? }" ,
1732+ _ => self . fatal ( format ! (
1733+ "unexpected {} type: {ty }" ,
17481734 match oop {
17491735 OverflowOp :: Add => "checked add" ,
17501736 OverflowOp :: Sub => "checked sub" ,
@@ -1753,13 +1739,17 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
17531739 ) ) ,
17541740 } ;
17551741
1756- let result = if is_add {
1757- self . add ( lhs, rhs)
1758- } else {
1759- self . sub ( lhs, rhs)
1760- } ;
1742+ // HACK(eddyb) SPIR-V `OpIAddCarry`/`OpISubBorrow` are specifically for
1743+ // unsigned overflow, so signed overflow still needs this custom logic.
1744+ if signed && let OverflowOp :: Add | OverflowOp :: Sub = oop {
1745+ let result = match oop {
1746+ OverflowOp :: Add => self . add ( lhs, rhs) ,
1747+ OverflowOp :: Sub => self . sub ( lhs, rhs) ,
1748+ OverflowOp :: Mul => unreachable ! ( ) ,
1749+ } ;
1750+
1751+ // adopted partially from https://github.com/ziglang/zig/blob/master/src/codegen/spirv.zig
17611752
1762- let overflowed = if signed {
17631753 // when adding, overflow could happen if
17641754 // - rhs is positive and result < lhs; or
17651755 // - rhs is negative and result > lhs
@@ -1771,30 +1761,80 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
17711761 // this is equivalent to (rhs < 0) == (result < lhs)
17721762 let rhs_lt_zero = self . icmp ( IntPredicate :: IntSLT , rhs, self . constant_int ( rhs. ty , 0 ) ) ;
17731763 let result_gt_lhs = self . icmp (
1774- if is_add {
1775- IntPredicate :: IntSGT
1776- } else {
1777- IntPredicate :: IntSLT
1764+ match oop {
1765+ OverflowOp :: Add => IntPredicate :: IntSGT ,
1766+ OverflowOp :: Sub => IntPredicate :: IntSLT ,
1767+ OverflowOp :: Mul => unreachable ! ( ) ,
17781768 } ,
17791769 result,
17801770 lhs,
17811771 ) ;
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
1772+
1773+ let overflowed = self . icmp ( IntPredicate :: IntEQ , rhs_lt_zero, result_gt_lhs) ;
1774+
1775+ return ( result, overflowed) ;
1776+ }
1777+
1778+ let result_type = self . layout_of ( ty) . spirv_type ( self . span ( ) , self ) ;
1779+ let pair_result_type = {
1780+ let field_types = [ result_type, result_type] ;
1781+ let ( field_offsets, size, align) = crate :: abi:: auto_struct_layout ( self , & field_types) ;
1782+ SpirvType :: Adt {
1783+ def_id : None ,
1784+ size,
1785+ align,
1786+ field_types : & field_types,
1787+ field_offsets : & field_offsets,
1788+ field_names : None ,
1789+ }
1790+ . def ( self . span ( ) , self )
1791+ } ;
1792+
1793+ let lhs = lhs. def ( self ) ;
1794+ let rhs = rhs. def ( self ) ;
1795+ let pair_result = match oop {
1796+ OverflowOp :: Add => self
1797+ . emit ( )
1798+ . i_add_carry ( pair_result_type, None , lhs, rhs)
1799+ . unwrap ( ) ,
1800+ OverflowOp :: Sub => self
1801+ . emit ( )
1802+ . i_sub_borrow ( pair_result_type, None , lhs, rhs)
1803+ . unwrap ( ) ,
1804+ OverflowOp :: Mul => {
1805+ if signed {
1806+ self . emit ( )
1807+ . s_mul_extended ( pair_result_type, None , lhs, rhs)
1808+ . unwrap ( )
17891809 } else {
1790- IntPredicate :: IntUGT
1791- } ,
1792- result,
1793- lhs,
1794- )
1810+ self . emit ( )
1811+ . u_mul_extended ( pair_result_type, None , lhs, rhs)
1812+ . unwrap ( )
1813+ }
1814+ }
1815+ }
1816+ . with_type ( pair_result_type) ;
1817+ let result_lo = self . extract_value ( pair_result, 0 ) ;
1818+ let result_hi = self . extract_value ( pair_result, 1 ) ;
1819+
1820+ // HACK(eddyb) SPIR-V lacks any `(T, T) -> (T, bool)` instructions,
1821+ // so instead `result_hi` is compared with the value expected in the
1822+ // non-overflow case (`0`, or `-1` for negative signed multiply result).
1823+ let expected_nonoverflowing_hi = match ( oop, signed) {
1824+ ( OverflowOp :: Add | OverflowOp :: Sub , _) | ( OverflowOp :: Mul , false ) => {
1825+ self . const_uint ( result_type, 0 )
1826+ }
1827+ ( OverflowOp :: Mul , true ) => {
1828+ // HACK(eddyb) `(x: iN) >> (N - 1)` will spread the sign bit
1829+ // across all `N` bits of `iN`, and should be equivalent to
1830+ // `if x < 0 { -1 } else { 0 }`, without needing compare+select).
1831+ let result_width = u32:: try_from ( self . int_width ( result_type) ) . unwrap ( ) ;
1832+ self . ashr ( result_lo, self . const_u32 ( result_width - 1 ) )
1833+ }
17951834 } ;
1835+ let overflowed = self . icmp ( IntPredicate :: IntNE , result_hi, expected_nonoverflowing_hi) ;
17961836
1797- ( result , overflowed)
1837+ ( result_lo , overflowed)
17981838 }
17991839
18001840 // rustc has the concept of an immediate vs. memory type - bools are compiled to LLVM bools as
0 commit comments