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