@@ -57,29 +57,40 @@ macro_rules! wrap_bitshift {
57
57
} ;
58
58
}
59
59
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
+ ///
65
70
macro_rules! int_divrem_guard {
66
71
( $lhs: ident,
67
72
$rhs: ident,
68
73
{ const PANIC_ZERO : & ' static str = $zero: literal;
69
- const PANIC_OVERFLOW : & ' static str = $overflow: literal;
70
74
$simd_call: ident
71
75
} ,
72
76
$int: ident ) => {
73
77
if $rhs. lanes_eq( Simd :: splat( 0 ) ) . any( ) {
74
78
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) ;
81
79
} 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) }
83
94
}
84
95
} ;
85
96
}
@@ -183,15 +194,13 @@ for_base_ops! {
183
194
impl Div :: div {
184
195
int_divrem_guard {
185
196
const PANIC_ZERO : & ' static str = "attempt to divide by zero" ;
186
- const PANIC_OVERFLOW : & ' static str = "attempt to divide with overflow" ;
187
197
simd_div
188
198
}
189
199
}
190
200
191
201
impl Rem :: rem {
192
202
int_divrem_guard {
193
203
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" ;
195
204
simd_rem
196
205
}
197
206
}
0 commit comments