From d65e45f6c931971e4360fdc5026c2c7f838286b4 Mon Sep 17 00:00:00 2001 From: h2-addad <hamza2.addad@live.uwe.ac.uk> Date: Sun, 16 Jul 2023 01:09:18 +0000 Subject: [PATCH] Update --- src/lib/arithmetic.c | 189 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 src/lib/arithmetic.c diff --git a/src/lib/arithmetic.c b/src/lib/arithmetic.c new file mode 100644 index 0000000..bfc9b5a --- /dev/null +++ b/src/lib/arithmetic.c @@ -0,0 +1,189 @@ +#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); +} -- GitLab