compiler_builtins/int/specialized_div_rem/
mod.rs

1// TODO: when `unsafe_block_in_unsafe_fn` is stabilized, remove this
2#![allow(unused_unsafe)]
3// The functions are complex with many branches, and explicit
4// `return`s makes it clear where function exit points are
5#![allow(clippy::needless_return)]
6#![allow(clippy::comparison_chain)]
7// Clippy is confused by the complex configuration
8#![allow(clippy::if_same_then_else)]
9#![allow(clippy::needless_bool)]
10
11//! This `specialized_div_rem` module is originally from version 1.0.0 of the
12//! `specialized-div-rem` crate. Note that `for` loops with ranges are not used in this
13//! module, since unoptimized compilation may generate references to `memcpy`.
14//!
15//! The purpose of these macros is to easily change the both the division algorithm used
16//! for a given integer size and the half division used by that algorithm. The way
17//! functions call each other is also constructed such that linkers will find the chain of
18//! software and hardware divisions needed for every size of signed and unsigned division.
19//! For example, most target compilations do the following:
20//!
21//!  - Many 128 bit division functions like `u128::wrapping_div` use
22//!    `std::intrinsics::unchecked_div`, which gets replaced by `__udivti3` because there
23//!    is not a 128 bit by 128 bit hardware division function in most architectures.
24//!    `__udivti3` uses `u128_div_rem` (this extra level of function calls exists because
25//!    `__umodti3` and `__udivmodti4` also exist, and `specialized_div_rem` supplies just
26//!    one function to calculate both the quotient and remainder. If configuration flags
27//!    enable it, `impl_trifecta!` defines `u128_div_rem` to use the trifecta algorithm,
28//!    which requires the half sized division `u64_by_u64_div_rem`. If the architecture
29//!    supplies a 64 bit hardware division instruction, `u64_by_u64_div_rem` will be
30//!    reduced to those instructions. Note that we do not specify the half size division
31//!    directly to be `__udivdi3`, because hardware division would never be introduced.
32//!  - If the architecture does not supply a 64 bit hardware division instruction, u64
33//!    divisions will use functions such as `__udivdi3`. This will call `u64_div_rem`
34//!    which is defined by `impl_delegate!`. The half division for this algorithm is
35//!    `u32_by_u32_div_rem` which in turn becomes hardware division instructions or more
36//!    software division algorithms.
37//!  - If the architecture does not supply a 32 bit hardware instruction, linkers will
38//!    look for `__udivsi3`. `impl_binary_long!` is used, but this  algorithm uses no half
39//!    division, so the chain of calls ends here.
40//!
41//! On some architectures like x86_64, an asymmetrically sized division is supplied, in
42//! which 128 bit numbers can be divided by 64 bit numbers. `impl_asymmetric!` is used to
43//! extend the 128 by 64 bit division to a full 128 by 128 bit division.
44
45// `allow(dead_code)` is used in various places, because the configuration code would otherwise be
46// ridiculously complex
47
48#[macro_use]
49mod norm_shift;
50
51#[macro_use]
52mod binary_long;
53
54#[macro_use]
55mod delegate;
56
57// used on SPARC
58#[allow(unused_imports)]
59#[cfg(not(feature = "unstable-public-internals"))]
60pub(crate) use self::delegate::u128_divide_sparc;
61#[cfg(feature = "unstable-public-internals")]
62pub use self::delegate::u128_divide_sparc;
63
64#[macro_use]
65mod trifecta;
66
67#[macro_use]
68mod asymmetric;
69
70/// The behavior of all divisions by zero is controlled by this function. This function should be
71/// impossible to reach by Rust users, unless `compiler-builtins` public division functions or
72/// `core/std::unchecked_div/rem` are directly used without a zero check in front.
73fn zero_div_fn() -> ! {
74    // Calling the intrinsic directly, to avoid the `assert_unsafe_precondition` that cannot be used
75    // here because it involves non-`inline` functions
76    // (https://github.com/rust-lang/compiler-builtins/issues/491).
77    unsafe { core::intrinsics::unreachable() }
78}
79
80const USE_LZ: bool = {
81    if cfg!(target_arch = "arm") {
82        if cfg!(target_feature = "thumb-mode") {
83            // ARM thumb targets have CLZ instructions if the instruction set of ARMv6T2 is
84            // supported. This is needed to successfully differentiate between targets like
85            // `thumbv8.base` and `thumbv8.main`.
86            cfg!(target_feature = "v6t2")
87        } else {
88            // Regular ARM targets have CLZ instructions if the ARMv5TE instruction set is
89            // supported. Technically, ARMv5T was the first to have CLZ, but the "v5t" target
90            // feature does not seem to work.
91            cfg!(target_feature = "v5te")
92        }
93    } else if cfg!(any(target_arch = "sparc", target_arch = "sparc64")) {
94        // LZD or LZCNT on SPARC only exists for the VIS 3 extension and later.
95        cfg!(target_feature = "vis3")
96    } else if cfg!(any(target_arch = "riscv32", target_arch = "riscv64")) {
97        // The 'Zbb' Basic Bit-Manipulation extension on RISC-V
98        // determines if a CLZ assembly instruction exists
99        cfg!(target_feature = "zbb")
100    } else {
101        // All other common targets Rust supports should have CLZ instructions
102        true
103    }
104};
105
106impl_normalization_shift!(
107    u32_normalization_shift,
108    USE_LZ,
109    32,
110    u32,
111    i32,
112    allow(dead_code)
113);
114impl_normalization_shift!(
115    u64_normalization_shift,
116    USE_LZ,
117    64,
118    u64,
119    i64,
120    allow(dead_code)
121);
122
123/// Divides `duo` by `div` and returns a tuple of the quotient and the remainder.
124/// `checked_div` and `checked_rem` are used to avoid bringing in panic function
125/// dependencies.
126#[inline]
127fn u64_by_u64_div_rem(duo: u64, div: u64) -> (u64, u64) {
128    if let Some(quo) = duo.checked_div(div) {
129        if let Some(rem) = duo.checked_rem(div) {
130            return (quo, rem);
131        }
132    }
133    zero_div_fn()
134}
135
136// Whether `trifecta` or `delegate` is faster for 128 bit division depends on the speed at which a
137// microarchitecture can multiply and divide. We decide to be optimistic and assume `trifecta` is
138// faster if the target pointer width is at least 64. Note that this
139// implementation is additionally included on WebAssembly despite the typical
140// pointer width there being 32 because it's typically run on a 64-bit machine
141// that has access to faster 64-bit operations.
142#[cfg(all(
143    any(
144        target_family = "wasm",
145        not(any(target_pointer_width = "16", target_pointer_width = "32")),
146    ),
147    not(all(not(feature = "no-asm"), target_arch = "x86_64")),
148    not(any(target_arch = "sparc", target_arch = "sparc64"))
149))]
150impl_trifecta!(
151    u128_div_rem,
152    zero_div_fn,
153    u64_by_u64_div_rem,
154    32,
155    u32,
156    u64,
157    u128
158);
159
160// If the pointer width less than 64 and this isn't wasm, then the target
161// architecture almost certainly does not have the fast 64 to 128 bit widening
162// multiplication needed for `trifecta` to be faster.
163#[cfg(all(
164    not(any(
165        target_family = "wasm",
166        not(any(target_pointer_width = "16", target_pointer_width = "32")),
167    )),
168    not(all(not(feature = "no-asm"), target_arch = "x86_64")),
169    not(any(target_arch = "sparc", target_arch = "sparc64"))
170))]
171impl_delegate!(
172    u128_div_rem,
173    zero_div_fn,
174    u64_normalization_shift,
175    u64_by_u64_div_rem,
176    32,
177    u32,
178    u64,
179    u128,
180    i128
181);
182
183/// Divides `duo` by `div` and returns a tuple of the quotient and the remainder.
184///
185/// # Safety
186///
187/// If the quotient does not fit in a `u64`, a floating point exception occurs.
188/// If `div == 0`, then a division by zero exception occurs.
189#[cfg(all(not(feature = "no-asm"), target_arch = "x86_64"))]
190#[inline]
191unsafe fn u128_by_u64_div_rem(duo: u128, div: u64) -> (u64, u64) {
192    let duo_lo = duo as u64;
193    let duo_hi = (duo >> 64) as u64;
194    let quo: u64;
195    let rem: u64;
196    unsafe {
197        // divides the combined registers rdx:rax (`duo` is split into two 64 bit parts to do this)
198        // by `div`. The quotient is stored in rax and the remainder in rdx.
199        // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust.
200        core::arch::asm!(
201            "div {0}",
202            in(reg) div,
203            inlateout("rax") duo_lo => quo,
204            inlateout("rdx") duo_hi => rem,
205            options(att_syntax, pure, nomem, nostack)
206        );
207    }
208    (quo, rem)
209}
210
211// use `asymmetric` instead of `trifecta` on x86_64
212#[cfg(all(not(feature = "no-asm"), target_arch = "x86_64"))]
213impl_asymmetric!(
214    u128_div_rem,
215    zero_div_fn,
216    u64_by_u64_div_rem,
217    u128_by_u64_div_rem,
218    32,
219    u32,
220    u64,
221    u128
222);
223
224/// Divides `duo` by `div` and returns a tuple of the quotient and the remainder.
225/// `checked_div` and `checked_rem` are used to avoid bringing in panic function
226/// dependencies.
227#[inline]
228#[allow(dead_code)]
229fn u32_by_u32_div_rem(duo: u32, div: u32) -> (u32, u32) {
230    if let Some(quo) = duo.checked_div(div) {
231        if let Some(rem) = duo.checked_rem(div) {
232            return (quo, rem);
233        }
234    }
235    zero_div_fn()
236}
237
238// When not on x86 and the pointer width is not 64, use `delegate` since the division size is larger
239// than register size.
240#[cfg(all(
241    not(all(not(feature = "no-asm"), target_arch = "x86")),
242    not(target_pointer_width = "64")
243))]
244impl_delegate!(
245    u64_div_rem,
246    zero_div_fn,
247    u32_normalization_shift,
248    u32_by_u32_div_rem,
249    16,
250    u16,
251    u32,
252    u64,
253    i64
254);
255
256// When not on x86 and the pointer width is 64, use `binary_long`.
257#[cfg(all(
258    not(all(not(feature = "no-asm"), target_arch = "x86")),
259    target_pointer_width = "64"
260))]
261impl_binary_long!(
262    u64_div_rem,
263    zero_div_fn,
264    u64_normalization_shift,
265    64,
266    u64,
267    i64
268);
269
270/// Divides `duo` by `div` and returns a tuple of the quotient and the remainder.
271///
272/// # Safety
273///
274/// If the quotient does not fit in a `u32`, a floating point exception occurs.
275/// If `div == 0`, then a division by zero exception occurs.
276#[cfg(all(not(feature = "no-asm"), target_arch = "x86"))]
277#[inline]
278unsafe fn u64_by_u32_div_rem(duo: u64, div: u32) -> (u32, u32) {
279    let duo_lo = duo as u32;
280    let duo_hi = (duo >> 32) as u32;
281    let quo: u32;
282    let rem: u32;
283    unsafe {
284        // divides the combined registers rdx:rax (`duo` is split into two 32 bit parts to do this)
285        // by `div`. The quotient is stored in rax and the remainder in rdx.
286        // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust.
287        core::arch::asm!(
288            "div {0}",
289            in(reg) div,
290            inlateout("rax") duo_lo => quo,
291            inlateout("rdx") duo_hi => rem,
292            options(att_syntax, pure, nomem, nostack)
293        );
294    }
295    (quo, rem)
296}
297
298// use `asymmetric` instead of `delegate` on x86
299#[cfg(all(not(feature = "no-asm"), target_arch = "x86"))]
300impl_asymmetric!(
301    u64_div_rem,
302    zero_div_fn,
303    u32_by_u32_div_rem,
304    u64_by_u32_div_rem,
305    16,
306    u16,
307    u32,
308    u64
309);
310
311// 32 bits is the smallest division used by `compiler-builtins`, so we end with binary long division
312impl_binary_long!(
313    u32_div_rem,
314    zero_div_fn,
315    u32_normalization_shift,
316    32,
317    u32,
318    i32,
319    allow(dead_code)
320);