blob: 6a35e0b53d446985a3666877fa0adfe647237b5f [file] [log] [blame]
#include <stdint.h>
extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *p_rem);
extern int64_t __divmoddi4(int64_t num, int64_t den, int64_t *p_rem);
extern int64_t __moddi3(int64_t num, int64_t den);
extern int64_t __divdi3(int64_t num, int64_t den);
extern uint64_t __udivdi3(uint64_t num, uint64_t den);
extern uint64_t __umoddi3(uint64_t num, uint64_t den);
#if __riscv
extern int __clzsi2(uint32_t num);
extern int __clzdi2(uint64_t num);
int __clzsi2(uint32_t num)
{
int n = 0;
while (num) {
++n;
num >>= 1;
}
return 32 - n;
}
int __clzdi2(uint64_t num)
{
return num >> 32 ? __clzsi2(num >> 32) : __clzsi2(num) + 32;
}
#endif
uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *p_rem)
{
uint64_t quot = 0;
/* Trigger a division by zero at run time (trick taken from iPXE). */
if (den == 0) {
if (p_rem)
*p_rem = 0;
return 1/((unsigned)den);
}
if (num >= den) {
/* Align den to num to avoid wasting time on leftmost zero bits. */
int n = __builtin_clzll(den) - __builtin_clzll(num);
den <<= n;
do {
quot <<= 1;
if (num >= den) {
num -= den;
quot |= 1;
}
den >>= 1;
} while (n--);
}
if (p_rem)
*p_rem = num;
return quot;
}
int64_t __divmoddi4(int64_t num, int64_t den, int64_t *p_rem)
{
int32_t nmask = num < 0 ? -1 : 0;
int32_t qmask = (num ^ den) < 0 ? -1 : 0;
uint64_t quot;
/* Compute absolute values and do an unsigned division. */
num = (num + nmask) ^ nmask;
if (den < 0)
den = -den;
/* Copy sign of num^den into quotient, sign of num into remainder. */
quot = (__udivmoddi4(num, den, (uint64_t *)p_rem) + qmask) ^ qmask;
if (p_rem)
*p_rem = (*p_rem + nmask) ^ nmask;
return quot;
}
int64_t __moddi3(int64_t num, int64_t den)
{
int64_t rem;
__divmoddi4(num, den, &rem);
return rem;
}
int64_t __divdi3(int64_t num, int64_t den)
{
int64_t rem;
return __divmoddi4(num, den, &rem);
}
uint64_t __udivdi3(uint64_t num, uint64_t den)
{
uint64_t rem;
return __udivmoddi4(num, den, &rem);
}
uint64_t __umoddi3(uint64_t num, uint64_t den)
{
uint64_t rem;
__udivmoddi4(num, den, &rem);
return rem;
}
#ifdef TEST
#include <assert.h>
#define UTEST(a, b, q, r) assert(__udivdi3(a, b) == q && __umoddi3(a, b) == r)
#define STEST(a, b, q, r) assert(__divdi3(a, b) == q && __moddi3(a, b) == r)
int main()
{
UTEST(1, 1, 1, 0);
UTEST(2, 2, 1, 0);
UTEST(5, 3, 1, 2);
UTEST(10, 3, 3, 1);
UTEST(120, 3, 40, 0);
UTEST(120, 1, 120, 0);
UTEST(0x7FFFFFFFFFFFFFFFULL, 17, 0x787878787878787, 8);
UTEST(0x7FFFFFFFFFFFFFFFULL, 0x787878787878787, 17, 8);
UTEST(0x8000000000000001ULL, 17, 0x787878787878787, 10);
UTEST(0x8000000000000001ULL, 0x787878787878787, 17, 10);
UTEST(0, 5, 0, 0);
STEST(0x7FFFFFFFFFFFFFFFULL, 17, 0x787878787878787, 8);
STEST(0x7FFFFFFFFFFFFFFFULL, -17, -0x787878787878787, 8);
STEST(-0x7FFFFFFFFFFFFFFFULL, 17, -0x787878787878787, -8);
STEST(-0x7FFFFFFFFFFFFFFFULL, -17, 0x787878787878787, -8);
STEST(33, 5, 6, 3);
STEST(33, -5, -6, 3);
STEST(-33, 5, -6, -3);
STEST(-33, -5, 6, -3);
}
#endif