190 lines
5.1 KiB
C
190 lines
5.1 KiB
C
|
#include <stdint.h>
|
|||
|
|
|||
|
/* On x86, division of one 64-bit integer by another cannot be
|
|||
|
done with a single instruction or a short sequence. Thus, GCC
|
|||
|
implements 64-bit division and remainder operations through
|
|||
|
function calls. These functions are normally obtained from
|
|||
|
libgcc, which is automatically included by GCC in any link
|
|||
|
that it does.
|
|||
|
|
|||
|
Some x86-64 machines, however, have a compiler and utilities
|
|||
|
that can generate 32-bit x86 code without having any of the
|
|||
|
necessary libraries, including libgcc. Thus, we can make
|
|||
|
Pintos work on these machines by simply implementing our own
|
|||
|
64-bit division routines, which are the only routines from
|
|||
|
libgcc that Pintos requires.
|
|||
|
|
|||
|
Completeness is another reason to include these routines. If
|
|||
|
Pintos is completely self-contained, then that makes it that
|
|||
|
much less mysterious. */
|
|||
|
|
|||
|
/* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to
|
|||
|
yield a 32-bit quotient. Returns the quotient.
|
|||
|
Traps with a divide error (#DE) if the quotient does not fit
|
|||
|
in 32 bits. */
|
|||
|
static inline uint32_t
|
|||
|
divl (uint64_t n, uint32_t d)
|
|||
|
{
|
|||
|
uint32_t n1 = n >> 32;
|
|||
|
uint32_t n0 = n;
|
|||
|
uint32_t q, r;
|
|||
|
|
|||
|
asm ("divl %4"
|
|||
|
: "=d" (r), "=a" (q)
|
|||
|
: "0" (n1), "1" (n0), "rm" (d));
|
|||
|
|
|||
|
return q;
|
|||
|
}
|
|||
|
|
|||
|
/* Returns the number of leading zero bits in X,
|
|||
|
which must be nonzero. */
|
|||
|
static int
|
|||
|
nlz (uint32_t x)
|
|||
|
{
|
|||
|
/* This technique is portable, but there are better ways to do
|
|||
|
it on particular systems. With sufficiently new enough GCC,
|
|||
|
you can use __builtin_clz() to take advantage of GCC's
|
|||
|
knowledge of how to do it. Or you can use the x86 BSR
|
|||
|
instruction directly. */
|
|||
|
int n = 0;
|
|||
|
if (x <= 0x0000FFFF)
|
|||
|
{
|
|||
|
n += 16;
|
|||
|
x <<= 16;
|
|||
|
}
|
|||
|
if (x <= 0x00FFFFFF)
|
|||
|
{
|
|||
|
n += 8;
|
|||
|
x <<= 8;
|
|||
|
}
|
|||
|
if (x <= 0x0FFFFFFF)
|
|||
|
{
|
|||
|
n += 4;
|
|||
|
x <<= 4;
|
|||
|
}
|
|||
|
if (x <= 0x3FFFFFFF)
|
|||
|
{
|
|||
|
n += 2;
|
|||
|
x <<= 2;
|
|||
|
}
|
|||
|
if (x <= 0x7FFFFFFF)
|
|||
|
n++;
|
|||
|
return n;
|
|||
|
}
|
|||
|
|
|||
|
/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
|
|||
|
quotient. */
|
|||
|
static uint64_t
|
|||
|
udiv64 (uint64_t n, uint64_t d)
|
|||
|
{
|
|||
|
if ((d >> 32) == 0)
|
|||
|
{
|
|||
|
/* Proof of correctness:
|
|||
|
|
|||
|
Let n, d, b, n1, and n0 be defined as in this function.
|
|||
|
Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d
|
|||
|
nonzero. Then:
|
|||
|
[n/d] = [n/d] - T + T
|
|||
|
= [n/d - T] + T by (1) below
|
|||
|
= [(b*n1 + n0)/d - T] + T by definition of n
|
|||
|
= [(b*n1 + n0)/d - dT/d] + T
|
|||
|
= [(b(n1 - d[n1/d]) + n0)/d] + T
|
|||
|
= [(b[n1 % d] + n0)/d] + T, by definition of %
|
|||
|
which is the expression calculated below.
|
|||
|
|
|||
|
(1) Note that for any real x, integer i: [x] + i = [x + i].
|
|||
|
|
|||
|
To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must
|
|||
|
be less than b. Assume that [n1 % d] and n0 take their
|
|||
|
respective maximum values of d - 1 and b - 1:
|
|||
|
[(b(d - 1) + (b - 1))/d] < b
|
|||
|
<=> [(bd - 1)/d] < b
|
|||
|
<=> [b - 1/d] < b
|
|||
|
which is a tautology.
|
|||
|
|
|||
|
Therefore, this code is correct and will not trap. */
|
|||
|
uint64_t b = 1ULL << 32;
|
|||
|
uint32_t n1 = n >> 32;
|
|||
|
uint32_t n0 = n;
|
|||
|
uint32_t d0 = d;
|
|||
|
|
|||
|
return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
/* Based on the algorithm and proof available from
|
|||
|
http://www.hackersdelight.org/revisions.pdf. */
|
|||
|
if (n < d)
|
|||
|
return 0;
|
|||
|
else
|
|||
|
{
|
|||
|
uint32_t d1 = d >> 32;
|
|||
|
int s = nlz (d1);
|
|||
|
uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s);
|
|||
|
return n - (q - 1) * d < d ? q - 1 : q;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
|
|||
|
remainder. */
|
|||
|
static uint32_t
|
|||
|
umod64 (uint64_t n, uint64_t d)
|
|||
|
{
|
|||
|
return n - d * udiv64 (n, d);
|
|||
|
}
|
|||
|
|
|||
|
/* Divides signed 64-bit N by signed 64-bit D and returns the
|
|||
|
quotient. */
|
|||
|
static int64_t
|
|||
|
sdiv64 (int64_t n, int64_t d)
|
|||
|
{
|
|||
|
uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n;
|
|||
|
uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d;
|
|||
|
uint64_t q_abs = udiv64 (n_abs, d_abs);
|
|||
|
return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs;
|
|||
|
}
|
|||
|
|
|||
|
/* Divides signed 64-bit N by signed 64-bit D and returns the
|
|||
|
remainder. */
|
|||
|
static int32_t
|
|||
|
smod64 (int64_t n, int64_t d)
|
|||
|
{
|
|||
|
return n - d * sdiv64 (n, d);
|
|||
|
}
|
|||
|
|
|||
|
/* These are the routines that GCC calls. */
|
|||
|
|
|||
|
long long __divdi3 (long long n, long long d);
|
|||
|
long long __moddi3 (long long n, long long d);
|
|||
|
unsigned long long __udivdi3 (unsigned long long n, unsigned long long d);
|
|||
|
unsigned long long __umoddi3 (unsigned long long n, unsigned long long d);
|
|||
|
|
|||
|
/* Signed 64-bit division. */
|
|||
|
long long
|
|||
|
__divdi3 (long long n, long long d)
|
|||
|
{
|
|||
|
return sdiv64 (n, d);
|
|||
|
}
|
|||
|
|
|||
|
/* Signed 64-bit remainder. */
|
|||
|
long long
|
|||
|
__moddi3 (long long n, long long d)
|
|||
|
{
|
|||
|
return smod64 (n, d);
|
|||
|
}
|
|||
|
|
|||
|
/* Unsigned 64-bit division. */
|
|||
|
unsigned long long
|
|||
|
__udivdi3 (unsigned long long n, unsigned long long d)
|
|||
|
{
|
|||
|
return udiv64 (n, d);
|
|||
|
}
|
|||
|
|
|||
|
/* Unsigned 64-bit remainder. */
|
|||
|
unsigned long long
|
|||
|
__umoddi3 (unsigned long long n, unsigned long long d)
|
|||
|
{
|
|||
|
return umod64 (n, d);
|
|||
|
}
|