xref: /freebsd/contrib/llvm-project/compiler-rt/lib/builtins/udivmoddi4.c (revision 04eeddc0aa8e0a417a16eaf9d7d095207f4a8623)
10b57cec5SDimitry Andric //===-- udivmoddi4.c - Implement __udivmoddi4 -----------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements __udivmoddi4 for the compiler_rt library.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "int_lib.h"
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric // Effects: if rem != 0, *rem = a % b
160b57cec5SDimitry Andric // Returns: a / b
170b57cec5SDimitry Andric 
180b57cec5SDimitry Andric // Translated from Figure 3-40 of The PowerPC Compiler Writer's Guide
190b57cec5SDimitry Andric 
2068d75effSDimitry Andric #if defined(_MSC_VER) && !defined(__clang__)
2168d75effSDimitry Andric // MSVC throws a warning about mod 0 here, disable it for builds that
2268d75effSDimitry Andric // warn-as-error
2368d75effSDimitry Andric #pragma warning(push)
24*04eeddc0SDimitry Andric #pragma warning(disable : 4723 4724)
2568d75effSDimitry Andric #endif
2668d75effSDimitry Andric 
270b57cec5SDimitry Andric COMPILER_RT_ABI du_int __udivmoddi4(du_int a, du_int b, du_int *rem) {
280b57cec5SDimitry Andric   const unsigned n_uword_bits = sizeof(su_int) * CHAR_BIT;
290b57cec5SDimitry Andric   const unsigned n_udword_bits = sizeof(du_int) * CHAR_BIT;
300b57cec5SDimitry Andric   udwords n;
310b57cec5SDimitry Andric   n.all = a;
320b57cec5SDimitry Andric   udwords d;
330b57cec5SDimitry Andric   d.all = b;
340b57cec5SDimitry Andric   udwords q;
350b57cec5SDimitry Andric   udwords r;
360b57cec5SDimitry Andric   unsigned sr;
370b57cec5SDimitry Andric   // special cases, X is unknown, K != 0
380b57cec5SDimitry Andric   if (n.s.high == 0) {
390b57cec5SDimitry Andric     if (d.s.high == 0) {
400b57cec5SDimitry Andric       // 0 X
410b57cec5SDimitry Andric       // ---
420b57cec5SDimitry Andric       // 0 X
430b57cec5SDimitry Andric       if (rem)
440b57cec5SDimitry Andric         *rem = n.s.low % d.s.low;
450b57cec5SDimitry Andric       return n.s.low / d.s.low;
460b57cec5SDimitry Andric     }
470b57cec5SDimitry Andric     // 0 X
480b57cec5SDimitry Andric     // ---
490b57cec5SDimitry Andric     // K X
500b57cec5SDimitry Andric     if (rem)
510b57cec5SDimitry Andric       *rem = n.s.low;
520b57cec5SDimitry Andric     return 0;
530b57cec5SDimitry Andric   }
540b57cec5SDimitry Andric   // n.s.high != 0
550b57cec5SDimitry Andric   if (d.s.low == 0) {
560b57cec5SDimitry Andric     if (d.s.high == 0) {
570b57cec5SDimitry Andric       // K X
580b57cec5SDimitry Andric       // ---
590b57cec5SDimitry Andric       // 0 0
600b57cec5SDimitry Andric       if (rem)
610b57cec5SDimitry Andric         *rem = n.s.high % d.s.low;
620b57cec5SDimitry Andric       return n.s.high / d.s.low;
630b57cec5SDimitry Andric     }
640b57cec5SDimitry Andric     // d.s.high != 0
650b57cec5SDimitry Andric     if (n.s.low == 0) {
660b57cec5SDimitry Andric       // K 0
670b57cec5SDimitry Andric       // ---
680b57cec5SDimitry Andric       // K 0
690b57cec5SDimitry Andric       if (rem) {
700b57cec5SDimitry Andric         r.s.high = n.s.high % d.s.high;
710b57cec5SDimitry Andric         r.s.low = 0;
720b57cec5SDimitry Andric         *rem = r.all;
730b57cec5SDimitry Andric       }
740b57cec5SDimitry Andric       return n.s.high / d.s.high;
750b57cec5SDimitry Andric     }
760b57cec5SDimitry Andric     // K K
770b57cec5SDimitry Andric     // ---
780b57cec5SDimitry Andric     // K 0
790b57cec5SDimitry Andric     if ((d.s.high & (d.s.high - 1)) == 0) /* if d is a power of 2 */ {
800b57cec5SDimitry Andric       if (rem) {
810b57cec5SDimitry Andric         r.s.low = n.s.low;
820b57cec5SDimitry Andric         r.s.high = n.s.high & (d.s.high - 1);
830b57cec5SDimitry Andric         *rem = r.all;
840b57cec5SDimitry Andric       }
850b57cec5SDimitry Andric       return n.s.high >> __builtin_ctz(d.s.high);
860b57cec5SDimitry Andric     }
870b57cec5SDimitry Andric     // K K
880b57cec5SDimitry Andric     // ---
890b57cec5SDimitry Andric     // K 0
905ffd83dbSDimitry Andric     sr = clzsi(d.s.high) - clzsi(n.s.high);
910b57cec5SDimitry Andric     // 0 <= sr <= n_uword_bits - 2 or sr large
920b57cec5SDimitry Andric     if (sr > n_uword_bits - 2) {
930b57cec5SDimitry Andric       if (rem)
940b57cec5SDimitry Andric         *rem = n.all;
950b57cec5SDimitry Andric       return 0;
960b57cec5SDimitry Andric     }
970b57cec5SDimitry Andric     ++sr;
980b57cec5SDimitry Andric     // 1 <= sr <= n_uword_bits - 1
990b57cec5SDimitry Andric     // q.all = n.all << (n_udword_bits - sr);
1000b57cec5SDimitry Andric     q.s.low = 0;
1010b57cec5SDimitry Andric     q.s.high = n.s.low << (n_uword_bits - sr);
1020b57cec5SDimitry Andric     // r.all = n.all >> sr;
1030b57cec5SDimitry Andric     r.s.high = n.s.high >> sr;
1040b57cec5SDimitry Andric     r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
1050b57cec5SDimitry Andric   } else /* d.s.low != 0 */ {
1060b57cec5SDimitry Andric     if (d.s.high == 0) {
1070b57cec5SDimitry Andric       // K X
1080b57cec5SDimitry Andric       // ---
1090b57cec5SDimitry Andric       // 0 K
1100b57cec5SDimitry Andric       if ((d.s.low & (d.s.low - 1)) == 0) /* if d is a power of 2 */ {
1110b57cec5SDimitry Andric         if (rem)
1120b57cec5SDimitry Andric           *rem = n.s.low & (d.s.low - 1);
1130b57cec5SDimitry Andric         if (d.s.low == 1)
1140b57cec5SDimitry Andric           return n.all;
1150b57cec5SDimitry Andric         sr = __builtin_ctz(d.s.low);
1160b57cec5SDimitry Andric         q.s.high = n.s.high >> sr;
1170b57cec5SDimitry Andric         q.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
1180b57cec5SDimitry Andric         return q.all;
1190b57cec5SDimitry Andric       }
1200b57cec5SDimitry Andric       // K X
1210b57cec5SDimitry Andric       // ---
1220b57cec5SDimitry Andric       // 0 K
1235ffd83dbSDimitry Andric       sr = 1 + n_uword_bits + clzsi(d.s.low) - clzsi(n.s.high);
1240b57cec5SDimitry Andric       // 2 <= sr <= n_udword_bits - 1
1250b57cec5SDimitry Andric       // q.all = n.all << (n_udword_bits - sr);
1260b57cec5SDimitry Andric       // r.all = n.all >> sr;
1270b57cec5SDimitry Andric       if (sr == n_uword_bits) {
1280b57cec5SDimitry Andric         q.s.low = 0;
1290b57cec5SDimitry Andric         q.s.high = n.s.low;
1300b57cec5SDimitry Andric         r.s.high = 0;
1310b57cec5SDimitry Andric         r.s.low = n.s.high;
1320b57cec5SDimitry Andric       } else if (sr < n_uword_bits) /* 2 <= sr <= n_uword_bits - 1 */ {
1330b57cec5SDimitry Andric         q.s.low = 0;
1340b57cec5SDimitry Andric         q.s.high = n.s.low << (n_uword_bits - sr);
1350b57cec5SDimitry Andric         r.s.high = n.s.high >> sr;
1360b57cec5SDimitry Andric         r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
1370b57cec5SDimitry Andric       } else /* n_uword_bits + 1 <= sr <= n_udword_bits - 1 */ {
1380b57cec5SDimitry Andric         q.s.low = n.s.low << (n_udword_bits - sr);
1390b57cec5SDimitry Andric         q.s.high = (n.s.high << (n_udword_bits - sr)) |
1400b57cec5SDimitry Andric                    (n.s.low >> (sr - n_uword_bits));
1410b57cec5SDimitry Andric         r.s.high = 0;
1420b57cec5SDimitry Andric         r.s.low = n.s.high >> (sr - n_uword_bits);
1430b57cec5SDimitry Andric       }
1440b57cec5SDimitry Andric     } else {
1450b57cec5SDimitry Andric       // K X
1460b57cec5SDimitry Andric       // ---
1470b57cec5SDimitry Andric       // K K
1485ffd83dbSDimitry Andric       sr = clzsi(d.s.high) - clzsi(n.s.high);
1490b57cec5SDimitry Andric       // 0 <= sr <= n_uword_bits - 1 or sr large
1500b57cec5SDimitry Andric       if (sr > n_uword_bits - 1) {
1510b57cec5SDimitry Andric         if (rem)
1520b57cec5SDimitry Andric           *rem = n.all;
1530b57cec5SDimitry Andric         return 0;
1540b57cec5SDimitry Andric       }
1550b57cec5SDimitry Andric       ++sr;
1560b57cec5SDimitry Andric       // 1 <= sr <= n_uword_bits
1570b57cec5SDimitry Andric       // q.all = n.all << (n_udword_bits - sr);
1580b57cec5SDimitry Andric       q.s.low = 0;
1590b57cec5SDimitry Andric       if (sr == n_uword_bits) {
1600b57cec5SDimitry Andric         q.s.high = n.s.low;
1610b57cec5SDimitry Andric         r.s.high = 0;
1620b57cec5SDimitry Andric         r.s.low = n.s.high;
1630b57cec5SDimitry Andric       } else {
1640b57cec5SDimitry Andric         q.s.high = n.s.low << (n_uword_bits - sr);
1650b57cec5SDimitry Andric         r.s.high = n.s.high >> sr;
1660b57cec5SDimitry Andric         r.s.low = (n.s.high << (n_uword_bits - sr)) | (n.s.low >> sr);
1670b57cec5SDimitry Andric       }
1680b57cec5SDimitry Andric     }
1690b57cec5SDimitry Andric   }
1700b57cec5SDimitry Andric   // Not a special case
1710b57cec5SDimitry Andric   // q and r are initialized with:
1720b57cec5SDimitry Andric   // q.all = n.all << (n_udword_bits - sr);
1730b57cec5SDimitry Andric   // r.all = n.all >> sr;
1740b57cec5SDimitry Andric   // 1 <= sr <= n_udword_bits - 1
1750b57cec5SDimitry Andric   su_int carry = 0;
1760b57cec5SDimitry Andric   for (; sr > 0; --sr) {
1770b57cec5SDimitry Andric     // r:q = ((r:q)  << 1) | carry
1780b57cec5SDimitry Andric     r.s.high = (r.s.high << 1) | (r.s.low >> (n_uword_bits - 1));
1790b57cec5SDimitry Andric     r.s.low = (r.s.low << 1) | (q.s.high >> (n_uword_bits - 1));
1800b57cec5SDimitry Andric     q.s.high = (q.s.high << 1) | (q.s.low >> (n_uword_bits - 1));
1810b57cec5SDimitry Andric     q.s.low = (q.s.low << 1) | carry;
1820b57cec5SDimitry Andric     // carry = 0;
1830b57cec5SDimitry Andric     // if (r.all >= d.all)
1840b57cec5SDimitry Andric     // {
1850b57cec5SDimitry Andric     //      r.all -= d.all;
1860b57cec5SDimitry Andric     //      carry = 1;
1870b57cec5SDimitry Andric     // }
1880b57cec5SDimitry Andric     const di_int s = (di_int)(d.all - r.all - 1) >> (n_udword_bits - 1);
1890b57cec5SDimitry Andric     carry = s & 1;
1900b57cec5SDimitry Andric     r.all -= d.all & s;
1910b57cec5SDimitry Andric   }
1920b57cec5SDimitry Andric   q.all = (q.all << 1) | carry;
1930b57cec5SDimitry Andric   if (rem)
1940b57cec5SDimitry Andric     *rem = r.all;
1950b57cec5SDimitry Andric   return q.all;
1960b57cec5SDimitry Andric }
19768d75effSDimitry Andric 
19868d75effSDimitry Andric #if defined(_MSC_VER) && !defined(__clang__)
19968d75effSDimitry Andric #pragma warning(pop)
20068d75effSDimitry Andric #endif
201