@@ -57,29 +57,40 @@ macro_rules! wrap_bitshift {
5757 } ;
5858}
5959
60- // Division by zero is poison, according to LLVM.
61- // So is dividing the MIN value of a signed integer by -1,
62- // since that would return MAX + 1.
63- // FIXME: Rust allows <SInt>::MIN / -1,
64- // so we should probably figure out how to make that safe.
60+ /// SAFETY: This macro must only be used to impl Div or Rem and given the matching intrinsic.
61+ /// It guards against LLVM's UB conditions for integer div or rem using masks and selects,
62+ /// thus guaranteeing a Rust value returns instead.
63+ ///
64+ /// | | LLVM | Rust
65+ /// | :--------------: | :--- | :----------
66+ /// | N {/,%} 0 | UB | panic!()
67+ /// | <$int>::MIN / -1 | UB | <$int>::MIN
68+ /// | <$int>::MIN % -1 | UB | 0
69+ ///
6570macro_rules! int_divrem_guard {
6671 ( $lhs: ident,
6772 $rhs: ident,
6873 { const PANIC_ZERO : & ' static str = $zero: literal;
69- const PANIC_OVERFLOW : & ' static str = $overflow: literal;
7074 $simd_call: ident
7175 } ,
7276 $int: ident ) => {
7377 if $rhs. lanes_eq( Simd :: splat( 0 ) ) . any( ) {
7478 panic!( $zero) ;
75- } else if <$int>:: MIN != 0
76- && ( $lhs. lanes_eq( Simd :: splat( <$int>:: MIN ) )
77- // type inference can break here, so cut an SInt to size
78- & $rhs. lanes_eq( Simd :: splat( -1i64 as _) ) ) . any( )
79- {
80- panic!( $overflow) ;
8179 } else {
82- unsafe { $crate:: simd:: intrinsics:: $simd_call( $lhs, $rhs) }
80+ // Prevent otherwise-UB overflow on the MIN / -1 case.
81+ let rhs = if <$int>:: MIN != 0 {
82+ // This should, at worst, optimize to a few branchless logical ops
83+ // Ideally, this entire conditional should evaporate
84+ // Fire LLVM and implement those manually if it doesn't get the hint
85+ ( $lhs. lanes_eq( Simd :: splat( <$int>:: MIN ) )
86+ // type inference can break here, so cut an SInt to size
87+ & $rhs. lanes_eq( Simd :: splat( -1i64 as _) ) )
88+ . select( Simd :: splat( 1 ) , $rhs)
89+ } else {
90+ // Nice base case to make it easy to const-fold away the other branch.
91+ $rhs
92+ } ;
93+ unsafe { $crate:: simd:: intrinsics:: $simd_call( $lhs, rhs) }
8394 }
8495 } ;
8596}
@@ -183,15 +194,13 @@ for_base_ops! {
183194 impl Div :: div {
184195 int_divrem_guard {
185196 const PANIC_ZERO : & ' static str = "attempt to divide by zero" ;
186- const PANIC_OVERFLOW : & ' static str = "attempt to divide with overflow" ;
187197 simd_div
188198 }
189199 }
190200
191201 impl Rem :: rem {
192202 int_divrem_guard {
193203 const PANIC_ZERO : & ' static str = "attempt to calculate the remainder with a divisor of zero" ;
194- const PANIC_OVERFLOW : & ' static str = "attempt to calculate the remainder with overflow" ;
195204 simd_rem
196205 }
197206 }
0 commit comments