PKUOS - Pintos
Pintos source browser for PKU Operating System course
arithmetic.c
Go to the documentation of this file.
1#include <stdint.h>
2
3/** On x86, division of one 64-bit integer by another cannot be
4 done with a single instruction or a short sequence. Thus, GCC
5 implements 64-bit division and remainder operations through
6 function calls. These functions are normally obtained from
7 libgcc, which is automatically included by GCC in any link
8 that it does.
9
10 Some x86-64 machines, however, have a compiler and utilities
11 that can generate 32-bit x86 code without having any of the
12 necessary libraries, including libgcc. Thus, we can make
13 Pintos work on these machines by simply implementing our own
14 64-bit division routines, which are the only routines from
15 libgcc that Pintos requires.
16
17 Completeness is another reason to include these routines. If
18 Pintos is completely self-contained, then that makes it that
19 much less mysterious. */
20
21/** Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to
22 yield a 32-bit quotient. Returns the quotient.
23 Traps with a divide error (#DE) if the quotient does not fit
24 in 32 bits. */
25static inline uint32_t
27{
28 uint32_t n1 = n >> 32;
29 uint32_t n0 = n;
30 uint32_t q, r;
31
32 asm ("divl %4"
33 : "=d" (r), "=a" (q)
34 : "0" (n1), "1" (n0), "rm" (d));
35
36 return q;
37}
38
39/** Returns the number of leading zero bits in X,
40 which must be nonzero. */
41static int
43{
44 /* This technique is portable, but there are better ways to do
45 it on particular systems. With sufficiently new enough GCC,
46 you can use __builtin_clz() to take advantage of GCC's
47 knowledge of how to do it. Or you can use the x86 BSR
48 instruction directly. */
49 int n = 0;
50 if (x <= 0x0000FFFF)
51 {
52 n += 16;
53 x <<= 16;
54 }
55 if (x <= 0x00FFFFFF)
56 {
57 n += 8;
58 x <<= 8;
59 }
60 if (x <= 0x0FFFFFFF)
61 {
62 n += 4;
63 x <<= 4;
64 }
65 if (x <= 0x3FFFFFFF)
66 {
67 n += 2;
68 x <<= 2;
69 }
70 if (x <= 0x7FFFFFFF)
71 n++;
72 return n;
73}
74
75/** Divides unsigned 64-bit N by unsigned 64-bit D and returns the
76 quotient. */
77static uint64_t
79{
80 if ((d >> 32) == 0)
81 {
82 /* Proof of correctness:
83
84 Let n, d, b, n1, and n0 be defined as in this function.
85 Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d
86 nonzero. Then:
87 [n/d] = [n/d] - T + T
88 = [n/d - T] + T by (1) below
89 = [(b*n1 + n0)/d - T] + T by definition of n
90 = [(b*n1 + n0)/d - dT/d] + T
91 = [(b(n1 - d[n1/d]) + n0)/d] + T
92 = [(b[n1 % d] + n0)/d] + T, by definition of %
93 which is the expression calculated below.
94
95 (1) Note that for any real x, integer i: [x] + i = [x + i].
96
97 To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must
98 be less than b. Assume that [n1 % d] and n0 take their
99 respective maximum values of d - 1 and b - 1:
100 [(b(d - 1) + (b - 1))/d] < b
101 <=> [(bd - 1)/d] < b
102 <=> [b - 1/d] < b
103 which is a tautology.
104
105 Therefore, this code is correct and will not trap. */
106 uint64_t b = 1ULL << 32;
107 uint32_t n1 = n >> 32;
108 uint32_t n0 = n;
109 uint32_t d0 = d;
110
111 return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0);
112 }
113 else
114 {
115 /* Based on the algorithm and proof available from
116 http://www.hackersdelight.org/revisions.pdf. */
117 if (n < d)
118 return 0;
119 else
120 {
121 uint32_t d1 = d >> 32;
122 int s = nlz (d1);
123 uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s);
124 return n - (q - 1) * d < d ? q - 1 : q;
125 }
126 }
127}
128
129/** Divides unsigned 64-bit N by unsigned 64-bit D and returns the
130 remainder. */
131static uint32_t
133{
134 return n - d * udiv64 (n, d);
135}
136
137/** Divides signed 64-bit N by signed 64-bit D and returns the
138 quotient. */
139static int64_t
141{
142 uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n;
143 uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d;
144 uint64_t q_abs = udiv64 (n_abs, d_abs);
145 return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs;
146}
147
148/** Divides signed 64-bit N by signed 64-bit D and returns the
149 remainder. */
150static int32_t
152{
153 return n - d * sdiv64 (n, d);
154}
155
156/** These are the routines that GCC calls. */
157
158long long __divdi3 (long long n, long long d);
159long long __moddi3 (long long n, long long d);
160unsigned long long __udivdi3 (unsigned long long n, unsigned long long d);
161unsigned long long __umoddi3 (unsigned long long n, unsigned long long d);
162
163/** Signed 64-bit division. */
164long long
165__divdi3 (long long n, long long d)
166{
167 return sdiv64 (n, d);
168}
169
170/** Signed 64-bit remainder. */
171long long
172__moddi3 (long long n, long long d)
173{
174 return smod64 (n, d);
175}
176
177/** Unsigned 64-bit division. */
178unsigned long long
179__udivdi3 (unsigned long long n, unsigned long long d)
180{
181 return udiv64 (n, d);
182}
183
184/** Unsigned 64-bit remainder. */
185unsigned long long
186__umoddi3 (unsigned long long n, unsigned long long d)
187{
188 return umod64 (n, d);
189}
static uint64_t udiv64(uint64_t n, uint64_t d)
Divides unsigned 64-bit N by unsigned 64-bit D and returns the quotient.
Definition: arithmetic.c:78
long long __moddi3(long long n, long long d)
Signed 64-bit remainder.
Definition: arithmetic.c:172
unsigned long long __udivdi3(unsigned long long n, unsigned long long d)
Unsigned 64-bit division.
Definition: arithmetic.c:179
unsigned long long __umoddi3(unsigned long long n, unsigned long long d)
Unsigned 64-bit remainder.
Definition: arithmetic.c:186
static int64_t sdiv64(int64_t n, int64_t d)
Divides signed 64-bit N by signed 64-bit D and returns the quotient.
Definition: arithmetic.c:140
static uint32_t divl(uint64_t n, uint32_t d)
On x86, division of one 64-bit integer by another cannot be done with a single instruction or a short...
Definition: arithmetic.c:26
static int nlz(uint32_t x)
Returns the number of leading zero bits in X, which must be nonzero.
Definition: arithmetic.c:42
static int32_t smod64(int64_t n, int64_t d)
Divides signed 64-bit N by signed 64-bit D and returns the remainder.
Definition: arithmetic.c:151
static uint32_t umod64(uint64_t n, uint64_t d)
Divides unsigned 64-bit N by unsigned 64-bit D and returns the remainder.
Definition: arithmetic.c:132
long long __divdi3(long long n, long long d)
These are the routines that GCC calls.
Definition: arithmetic.c:165
static char x
Verifies that mapping over the data segment is disallowed.
Definition: mmap-over-data.c:9
static uint8_t s[256]
RC4-based pseudo-random number generator (PRNG).
Definition: random.c:17
unsigned int uint32_t
Definition: stdint.h:26
signed long long int int64_t
Definition: stdint.h:16
signed int int32_t
Definition: stdint.h:12
unsigned long long int uint64_t
Definition: stdint.h:29