2003-07-05 23:19:41

by Bernardo Innocenti

[permalink] [raw]
Subject: [PATCH] Fix do_div() for all architectures

Hello everybody,

second iteration of the div64.h cleanup + bug fixing.
Contains the following changes since the previous release:

- add likely() to the fast path (divisor>>32 == 0);

- add __attribute__((pure)) to __div64_32() prototype so
the compiler knows global memory isn't clobbered;

- export the __div64_32 symbol for modules;

The generic do_div() code is now tested on m68knommu, arm
and even i386 (by manually replacing real asm-i386/div64.h
with the generic version).

Applies to 2.5.74 and 2.5.74-bk2. I'll provide a 2.4
backport once it has been tested in 2.5.

// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

-----------------------------------------------------------------

- add generic C implementations of the do_div() for 32bit and 64bit
archs in asm-generic/div64.h;

- add generic library support function __div64_32() to handle the
full 64/32 case on 32bit archs;

- kill multiple copies of generic do_div() in architecture
specific subdirs. Most copies were either buggy or not doing
what they were supposed to do;

- ensure all surviving instances of do_div() have their parameters
correctly parenthesized to avoid funny side-effects;

include/asm-generic/div64.h | 53 ++++++++++++++++++++++++++++++++++++++++
lib/div64.c | 47 ++++++++++++++++++++++++++++++++++
include/asm-alpha/div64.h | 15 -----------
include/asm-arm26/div64.h | 15 -----------
include/asm-cris/div64.h | 17 ------------
include/asm-h8300/div64.h | 14 ----------
include/asm-ia64/div64.h | 21 ----------------
include/asm-m68k/div64.h | 9 ------
include/asm-m68knommu/div64.h | 14 ----------
include/asm-mips64/div64.h | 19 --------------
include/asm-parisc/div64.h | 55 ------------------------------------------
include/asm-ppc/div64.h | 24 ------------------
include/asm-ppc64/div64.h | 19 --------------
include/asm-s390/div64.h | 8 ------
include/asm-sh/div64.h | 21 ----------------
include/asm-sparc/div64.h | 12 ---------
include/asm-sparc64/div64.h | 15 -----------
include/asm-v850/div64.h | 12 ---------
include/asm-x86_64/div64.h | 15 -----------
lib/Makefile | 2 -
20 files changed, 115 insertions(+), 290 deletions(-)

diff -Nru linux-2.5.74-uc0/include/asm-generic/div64.h linux-2.5.74-uc0-develer/include/asm-generic/div64.h
--- linux-2.5.74-uc0/include/asm-generic/div64.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5.74-uc0-develer/include/asm-generic/div64.h 2003-07-03 09:55:04.000000000 +0200
@@ -0,0 +1,53 @@
+#ifndef _ASM_GENERIC_DIV64_H
+#define _ASM_GENERIC_DIV64_H
+/*
+ * Copyright (C) 2003 Bernardo Innocenti <[email protected]>
+ * Based on former asm-ppc/div64.h and asm-m68knommu/div64.h
+ *
+ * The semantics of do_div() are:
+ *
+ * uint32_t do_div(uint64_t *n, uint32_t base)
+ * {
+ * uint32_t remainder = *n % base;
+ * *n = *n / base;
+ * return remainder;
+ * }
+ *
+ * NOTE: macro parameter n is evaluated multiple times,
+ * beware of side effects!
+ */
+
+#include <linux/types.h>
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({ \
+ uint32_t __base = (base); \
+ uint32_t __rem; \
+ __rem = ((uint64_t)(n)) % __base; \
+ (n) = ((uint64_t)(n)) / __base; \
+ __rem; \
+ })
+
+#elif BITS_PER_LONG == 32
+
+extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor) __attribute__((pure));
+
+# define do_div(n,base) ({ \
+ uint32_t __base = (base); \
+ uint32_t __rem; \
+ if (likely(((n) >> 32) == 0)) { \
+ __rem = (uint32_t)(n) % __base; \
+ (n) = (uint32_t)(n) / __base; \
+ } else \
+ __rem = __div64_32(&(n), __base); \
+ __rem; \
+ })
+
+#else /* BITS_PER_LONG == ?? */
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+#endif /* _ASM_GENERIC_DIV64_H */
diff -Nru linux-2.5.74-uc0/lib/div64.c linux-2.5.74-uc0-develer/lib/div64.c
--- linux-2.5.74-uc0/lib/div64.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5.74-uc0-develer/lib/div64.c 2003-07-03 09:37:22.000000000 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2003 Bernardo Innocenti <[email protected]>
+ *
+ * Based on former do_div() implementation from asm-parisc/div64.h:
+ * Copyright (C) 1999 Hewlett-Packard Co
+ * Copyright (C) 1999 David Mosberger-Tang <[email protected]>
+ *
+ *
+ * Generic C version of 64bit/32bit division and modulo, with
+ * 64bit result and 32bit remainder.
+ *
+ * The fast case for (n>>32 == 0) is handled inline by do_div().
+ *
+ * Code generated for this function might be very inefficient
+ * for some CPUs. __div64_32() can be overridden by linking arch-specific
+ * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <asm/div64.h>
+
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+ uint32_t low, low2, high, rem;
+
+ low = *n & 0xffffffff;
+ high = *n >> 32;
+ rem = high % (uint32_t)base;
+ high = high / (uint32_t)base;
+ low2 = low >> 16;
+ low2 += rem << 16;
+ rem = low2 % (uint32_t)base;
+ low2 = low2 / (uint32_t)base;
+ low = low & 0xffff;
+ low += rem << 16;
+ rem = low % (uint32_t)base;
+ low = low / (uint32_t)base;
+
+ *n = low +
+ ((uint64_t)low2 << 16) +
+ ((uint64_t)high << 32);
+
+ return rem;
+}
+
+EXPORT_SYMBOL(__div64_32);
diff -Nru linux-2.5.74-uc0/include/asm-alpha/div64.h linux-2.5.74-uc0-develer/include/asm-alpha/div64.h
--- linux-2.5.74-uc0/include/asm-alpha/div64.h 2003-07-02 22:54:43.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-alpha/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __ALPHA_DIV64
-#define __ALPHA_DIV64
-
-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- __res; })
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-arm26/div64.h linux-2.5.74-uc0-develer/include/asm-arm26/div64.h
--- linux-2.5.74-uc0/include/asm-arm26/div64.h 2003-07-02 22:42:11.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-arm26/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __ASM_ARM_DIV64
-#define __ASM_ARM_DIV64
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) \
-({ \
- int __res; \
- __res = ((unsigned long)n) % (unsigned int)base; \
- n = ((unsigned long)n) / (unsigned int)base; \
- __res; \
-})
-
-#endif
-
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-cris/div64.h linux-2.5.74-uc0-develer/include/asm-cris/div64.h
--- linux-2.5.74-uc0/include/asm-cris/div64.h 2003-07-02 22:42:06.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-cris/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,16 +1 @@
-#ifndef __ASM_CRIS_DIV64
-#define __ASM_CRIS_DIV64
-
-/* copy from asm-arm */
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) \
-({ \
- int __res; \
- __res = ((unsigned long)n) % (unsigned int)base; \
- n = ((unsigned long)n) / (unsigned int)base; \
- __res; \
-})
-
-#endif
-
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-h8300/div64.h linux-2.5.74-uc0-develer/include/asm-h8300/div64.h
--- linux-2.5.74-uc0/include/asm-h8300/div64.h 2003-07-02 22:39:25.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-h8300/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,13 +1 @@
-#ifndef H8300_DIV64_H
-#define H8300_DIV64_H
-
-/* n = n / base; return rem; */
-
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; \
-})
-
-#endif /* _H8300_DIV64_H */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-ia64/div64.h linux-2.5.74-uc0-develer/include/asm-ia64/div64.h
--- linux-2.5.74-uc0/include/asm-ia64/div64.h 2003-07-02 22:58:14.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-ia64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,20 +1 @@
-#ifndef _ASM_IA64_DIV64_H
-#define _ASM_IA64_DIV64_H
-
-/*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <[email protected]>
- *
- * vsprintf uses this to divide a 64-bit integer N by a small integer BASE.
- * This is incredibly hard on IA-64...
- */
-
-#define do_div(n,base) \
-({ \
- int _res; \
- _res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- _res; \
-})
-
-#endif /* _ASM_IA64_DIV64_H */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-m68k/div64.h linux-2.5.74-uc0-develer/include/asm-m68k/div64.h
--- linux-2.5.74-uc0/include/asm-m68k/div64.h 2003-07-02 22:55:53.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-m68k/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -3,7 +3,6 @@

/* n = n / base; return rem; */

-#if 1
#define do_div(n, base) ({ \
union { \
unsigned long n32[2]; \
@@ -23,13 +22,5 @@
(n) = __n.n64; \
__rem; \
})
-#else
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; \
-})
-#endif

#endif /* _M68K_DIV64_H */
diff -Nru linux-2.5.74-uc0/include/asm-m68knommu/div64.h linux-2.5.74-uc0-develer/include/asm-m68knommu/div64.h
--- linux-2.5.74-uc0/include/asm-m68knommu/div64.h 2003-07-03 10:40:35.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-m68knommu/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,13 +1 @@
-#ifndef _M68KNOMMU_DIV64_H
-#define _M68KNOMMU_DIV64_H
-
-/* n = n / base; return rem; */
-
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; \
-})
-
-#endif /* _M68K_DIV64_H */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-mips64/div64.h linux-2.5.74-uc0-develer/include/asm-mips64/div64.h
--- linux-2.5.74-uc0/include/asm-mips64/div64.h 2003-07-02 22:47:26.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-mips64/div64.h 2003-07-03 09:39:28.000000000 +0200
@@ -27,23 +27,6 @@
(res) = __quot; \
__mod; })

-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n, base) ({ \
- unsigned long __quot; \
- unsigned int __mod; \
- unsigned long __div; \
- unsigned int __base; \
- \
- __div = (n); \
- __base = (base); \
- \
- __mod = __div % __base; \
- __quot = __div / __base; \
- \
- (n) = __quot; \
- __mod; })
+#include <asm-generic.h>

#endif /* _ASM_DIV64_H */
diff -Nru linux-2.5.74-uc0/include/asm-parisc/div64.h linux-2.5.74-uc0-develer/include/asm-parisc/div64.h
--- linux-2.5.74-uc0/include/asm-parisc/div64.h 2003-07-02 22:48:40.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-parisc/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,54 +1 @@
-#ifndef __ASM_PARISC_DIV64
-#define __ASM_PARISC_DIV64
-
-#ifdef __LP64__
-
-/*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <[email protected]>
- *
- * vsprintf uses this to divide a 64-bit integer N by a small integer BASE.
- * This is incredibly hard on IA-64 and HPPA
- */
-
-#define do_div(n,base) \
-({ \
- int _res; \
- _res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- _res; \
-})
-
-#else
-/*
- * unsigned long long division. Yuck Yuck! What is Linux coming to?
- * This is 100% disgusting
- */
-#define do_div(n,base) \
-({ \
- unsigned long __low, __low2, __high, __rem; \
- __low = (n) & 0xffffffff; \
- __high = (n) >> 32; \
- if (__high) { \
- __rem = __high % (unsigned long)base; \
- __high = __high / (unsigned long)base; \
- __low2 = __low >> 16; \
- __low2 += __rem << 16; \
- __rem = __low2 % (unsigned long)base; \
- __low2 = __low2 / (unsigned long)base; \
- __low = __low & 0xffff; \
- __low += __rem << 16; \
- __rem = __low % (unsigned long)base; \
- __low = __low / (unsigned long)base; \
- n = __low + ((long long)__low2 << 16) + \
- ((long long) __high << 32); \
- } else { \
- __rem = __low % (unsigned long)base; \
- n = (__low / (unsigned long)base); \
- } \
- __rem; \
-})
-#endif
-
-#endif
-
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-ppc/div64.h linux-2.5.74-uc0-develer/include/asm-ppc/div64.h
--- linux-2.5.74-uc0/include/asm-ppc/div64.h 2003-07-02 22:57:06.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-ppc/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,23 +1 @@
-#ifndef __PPC_DIV64
-#define __PPC_DIV64
-
-#include <linux/types.h>
-
-extern u32 __div64_32(u64 *dividend, u32 div);
-
-#define do_div(n, div) ({ \
- u64 __n = (n); \
- u32 __d = (div); \
- u32 __q, __r; \
- if ((__n >> 32) == 0) { \
- __q = (u32)__n / __d; \
- __r = (u32)__n - __q * __d; \
- (n) = __q; \
- } else { \
- __r = __div64_32(&__n, __d); \
- (n) = __n; \
- } \
- __r; \
-})
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-ppc64/div64.h linux-2.5.74-uc0-develer/include/asm-ppc64/div64.h
--- linux-2.5.74-uc0/include/asm-ppc64/div64.h 2003-07-02 22:39:34.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-ppc64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,18 +1 @@
-#ifndef __PPC_DIV64
-#define __PPC_DIV64
-
-/* Copyright 2001 PPC64 Team, IBM Corp
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- __res; })
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-s390/div64.h linux-2.5.74-uc0-develer/include/asm-s390/div64.h
--- linux-2.5.74-uc0/include/asm-s390/div64.h 2003-07-02 22:50:17.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-s390/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -43,13 +43,7 @@
})

#else /* __s390x__ */
-
-#define do_div(n,base) ({ \
-int __res; \
-__res = ((unsigned long) n) % (unsigned) base; \
-n = ((unsigned long) n) / (unsigned) base; \
-__res; })
-
+#include <asm-generic/div64.h>
#endif /* __s390x__ */

#endif
diff -Nru linux-2.5.74-uc0/include/asm-sh/div64.h linux-2.5.74-uc0-develer/include/asm-sh/div64.h
--- linux-2.5.74-uc0/include/asm-sh/div64.h 2003-07-02 22:39:36.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-sh/div64.h 2003-07-03 09:38:59.000000000 +0200
@@ -1,20 +1 @@
-#ifndef __ASM_SH_DIV64
-#define __ASM_SH_DIV64
-
-extern u64 __div64_32(u64 n, u32 d);
-
-#define do_div(n,base) ({ \
-u64 __n = (n), __q; \
-u32 __base = (base); \
-u32 __res; \
-if ((__n >> 32) == 0) { \
- __res = ((unsigned long) __n) % (unsigned) __base; \
- (n) = ((unsigned long) __n) / (unsigned) __base; \
-} else { \
- __q = __div64_32(__n, __base); \
- __res = __n - __q * __base; \
- (n) = __q; \
-} \
-__res; })
-
-#endif /* __ASM_SH_DIV64 */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-sparc/div64.h linux-2.5.74-uc0-develer/include/asm-sparc/div64.h
--- linux-2.5.74-uc0/include/asm-sparc/div64.h 2003-07-02 22:51:01.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-sparc/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,11 +1 @@
-#ifndef __SPARC_DIV64
-#define __SPARC_DIV64
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
-
-#endif /* __SPARC_DIV64 */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-sparc64/div64.h linux-2.5.74-uc0-develer/include/asm-sparc64/div64.h
--- linux-2.5.74-uc0/include/asm-sparc64/div64.h 2003-07-02 22:46:06.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-sparc64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __SPARC64_DIV64
-#define __SPARC64_DIV64
-
-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
-
-#endif /* __SPARC64_DIV64 */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-v850/div64.h linux-2.5.74-uc0-develer/include/asm-v850/div64.h
--- linux-2.5.74-uc0/include/asm-v850/div64.h 2003-07-02 22:53:46.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-v850/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,11 +1 @@
-#ifndef __V850_DIV64_H__
-#define __V850_DIV64_H__
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
-
-#endif /* __V850_DIV64_H__ */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/include/asm-x86_64/div64.h linux-2.5.74-uc0-develer/include/asm-x86_64/div64.h
--- linux-2.5.74-uc0/include/asm-x86_64/div64.h 2003-07-02 22:54:30.000000000 +0200
+++ linux-2.5.74-uc0-develer/include/asm-x86_64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __X86_64_DIV64
-#define __X86_64_DIV64
-
-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- __res; })
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74-uc0/lib/Makefile linux-2.5.74-uc0-develer/lib/Makefile
--- linux-2.5.74-uc0/lib/Makefile 2003-07-02 22:40:29.000000000 +0200
+++ linux-2.5.74-uc0-develer/lib/Makefile 2003-07-03 09:37:22.000000000 +0200
@@ -5,7 +5,7 @@

lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
- kobject.o idr.o
+ kobject.o idr.o div64.o

lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o




2003-07-06 07:33:40

by Russell King

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Sun, Jul 06, 2003 at 01:33:15AM +0200, Bernardo Innocenti wrote:
> Hello everybody,
>
> second iteration of the div64.h cleanup + bug fixing.
> Contains the following changes since the previous release:

arm26 is Ian Molton. Please copy Ian on ARM26 matters, not myself.
Thanks.

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html

2003-07-06 15:26:19

by Ian molton

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Sun, 6 Jul 2003 08:47:50 +0100
Russell King <[email protected]> wrote:

>
> > second iteration of the div64.h cleanup + bug fixing.
> > Contains the following changes since the previous release:
>
> arm26 is Ian Molton. Please copy Ian on ARM26 matters, not myself.
> Thanks.

FWIW, theres a patch to MAINTAINERS on its way to linus since a week ago
:-)

John Appleby might like to be copied on ARM26 things also.

--
Spyros lair: http://www.mnementh.co.uk/ |||| Maintainer: arm26 linux

Do not meddle in the affairs of Dragons, for you are tasty and good with
ketchup.

2003-07-07 04:13:55

by Bernardo Innocenti

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Sunday 06 July 2003 01:33, Bernardo Innocenti wrote:

> - add __attribute__((pure)) to __div64_32() prototype so
> the compiler knows global memory isn't clobbered;

Hmmm... I've just found out that the pure attribute wasn't
supported until gcc 2.96. Shall I get rid of it or maybe add
something in linux/compiler.h?

Please note that __attribute__((const)) is not applicable
to this case according to gcc documentation.

--
// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

Please don't send Word attachments - http://www.gnu.org/philosophy/no-word-attachments.html


2003-07-08 18:15:06

by Bernardo Innocenti

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Sunday 06 July 2003 01:33, Bernardo Innocenti wrote:

> > - add __attribute__((pure)) to __div64_32() prototype so
> > the compiler knows global memory isn't clobbered;
>
> Hmmm... I've just found out that the pure attribute wasn't
> supported until gcc 2.96. Shall I get rid of it or maybe add
> something in linux/compiler.h?

Ok, I've now placed a new __attribute_pure__ macro in
linux/compiler.h to workaround this. New patch follows.

Andrew, would you like to pick this patch up for me and forward it
to Linus after it received some testing in -mm?

-----------------------------------------------------------------

- add generic C implementations of the do_div() for 32bit and 64bit
archs in asm-generic/div64.h;

- add generic library support function __div64_32() to handle the
full 64/32 case on 32bit archs;

- kill multiple copies of generic do_div() in architecture
specific subdirs. Most copies were either buggy or not doing
what they were supposed to do;

- ensure all surviving instances of do_div() have their parameters
correctly parenthesized to avoid funny side-effects;

include/asm-alpha/div64.h | 15 -----------
include/asm-arm26/div64.h | 15 -----------
include/asm-cris/div64.h | 17 ------------
include/asm-generic/div64.h | 54 +++++++++++++++++++++++++++++++++++++++++
include/asm-h8300/div64.h | 14 ----------
include/asm-ia64/div64.h | 21 ----------------
include/asm-m68k/div64.h | 9 ------
include/asm-m68knommu/div64.h | 14 ----------
include/asm-mips64/div64.h | 19 --------------
include/asm-parisc/div64.h | 55 ------------------------------------------
include/asm-ppc/div64.h | 24 ------------------
include/asm-ppc64/div64.h | 19 --------------
include/asm-s390/div64.h | 8 ------
include/asm-sh/div64.h | 21 ----------------
include/asm-sparc/div64.h | 12 ---------
include/asm-sparc64/div64.h | 15 -----------
include/asm-v850/div64.h | 12 ---------
include/asm-x86_64/div64.h | 15 -----------
include/linux/compiler.h | 18 +++++++++++++
lib/Makefile | 2 -
lib/div64.c | 52 +++++++++++++++++++++++++++++++++++++++
21 files changed, 141 insertions(+), 290 deletions(-)

diff -Nru linux-2.5.74.orig/include/asm-generic/div64.h linux-2.5.74/include/asm-generic/div64.h
--- linux-2.5.74.orig/include/asm-generic/div64.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5.74/include/asm-generic/div64.h 2003-07-03 09:55:04.000000000 +0200
@@ -0,0 +1,54 @@
+#ifndef _ASM_GENERIC_DIV64_H
+#define _ASM_GENERIC_DIV64_H
+/*
+ * Copyright (C) 2003 Bernardo Innocenti <[email protected]>
+ * Based on former asm-ppc/div64.h and asm-m68knommu/div64.h
+ *
+ * The semantics of do_div() are:
+ *
+ * uint32_t do_div(uint64_t *n, uint32_t base)
+ * {
+ * uint32_t remainder = *n % base;
+ * *n = *n / base;
+ * return remainder;
+ * }
+ *
+ * NOTE: macro parameter n is evaluated multiple times,
+ * beware of side effects!
+ */
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+
+#if BITS_PER_LONG == 64
+
+# define do_div(n,base) ({ \
+ uint32_t __base = (base); \
+ uint32_t __rem; \
+ __rem = ((uint64_t)(n)) % __base; \
+ (n) = ((uint64_t)(n)) / __base; \
+ __rem; \
+ })
+
+#elif BITS_PER_LONG == 32
+
+extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor) __attribute_pure__;
+
+# define do_div(n,base) ({ \
+ uint32_t __base = (base); \
+ uint32_t __rem; \
+ if (likely(((n) >> 32) == 0)) { \
+ __rem = (uint32_t)(n) % __base; \
+ (n) = (uint32_t)(n) / __base; \
+ } else \
+ __rem = __div64_32(&(n), __base); \
+ __rem; \
+ })
+
+#else /* BITS_PER_LONG == ?? */
+
+# error do_div() does not yet support the C64
+
+#endif /* BITS_PER_LONG */
+
+#endif /* _ASM_GENERIC_DIV64_H */
diff -Nru linux-2.5.74.orig/include/linux/compiler.h linux-2.5.74/include/linux/compiler.h
--- linux-2.5.74.orig/include/linux/compiler.h 2003-07-02 22:50:12.000000000 +0200
+++ linux-2.5.74/include/linux/compiler.h 2003-07-08 19:24:41.000000000 +0200
@@ -56,6 +56,24 @@
#define __attribute_used__ __attribute__((__unused__))
#endif

+/*
+ * From the GCC manual:
+ *
+ * Many functions have no effects except the return value and their
+ * return value depends only on the parameters and/or global
+ * variables. Such a function can be subject to common subexpression
+ * elimination and loop optimization just as an arithmetic operator
+ * would be.
+ * [...]
+ * The attribute `pure' is not implemented in GCC versions earlier
+ * than 2.96.
+ */
+#if (__GNUC__ == 2 && __GNUC_MINOR >= 96) || __GNUC__ > 2
+#define __attribute_pure__ __attribute__((pure))
+#else
+#define __attribute_pure__ /* unimplemented */
+#endif
+
/* This macro obfuscates arithmetic on a variable address so that gcc
shouldn't recognize the original var, and make assumptions about it */
#define RELOC_HIDE(ptr, off) \
diff -Nru linux-2.5.74.orig/lib/div64.c linux-2.5.74/lib/div64.c
--- linux-2.5.74.orig/lib/div64.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.5.74/lib/div64.c 2003-07-03 09:37:22.000000000 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2003 Bernardo Innocenti <[email protected]>
+ *
+ * Based on former do_div() implementation from asm-parisc/div64.h:
+ * Copyright (C) 1999 Hewlett-Packard Co
+ * Copyright (C) 1999 David Mosberger-Tang <[email protected]>
+ *
+ *
+ * Generic C version of 64bit/32bit division and modulo, with
+ * 64bit result and 32bit remainder.
+ *
+ * The fast case for (n>>32 == 0) is handled inline by do_div().
+ *
+ * Code generated for this function might be very inefficient
+ * for some CPUs. __div64_32() can be overridden by linking arch-specific
+ * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <asm/div64.h>
+
+/* Not needed on 64bit architectures */
+#if BITS_PER_LONG == 32
+
+uint32_t __div64_32(uint64_t *n, uint32_t base)
+{
+ uint32_t low, low2, high, rem;
+
+ low = *n & 0xffffffff;
+ high = *n >> 32;
+ rem = high % (uint32_t)base;
+ high = high / (uint32_t)base;
+ low2 = low >> 16;
+ low2 += rem << 16;
+ rem = low2 % (uint32_t)base;
+ low2 = low2 / (uint32_t)base;
+ low = low & 0xffff;
+ low += rem << 16;
+ rem = low % (uint32_t)base;
+ low = low / (uint32_t)base;
+
+ *n = low +
+ ((uint64_t)low2 << 16) +
+ ((uint64_t)high << 32);
+
+ return rem;
+}
+
+EXPORT_SYMBOL(__div64_32);
+
+#endif /* BITS_PER_LONG == 32 */
diff -Nru linux-2.5.74.orig/include/asm-alpha/div64.h linux-2.5.74/include/asm-alpha/div64.h
--- linux-2.5.74.orig/include/asm-alpha/div64.h 2003-07-02 22:54:43.000000000 +0200
+++ linux-2.5.74/include/asm-alpha/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __ALPHA_DIV64
-#define __ALPHA_DIV64
-
-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- __res; })
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-arm26/div64.h linux-2.5.74/include/asm-arm26/div64.h
--- linux-2.5.74.orig/include/asm-arm26/div64.h 2003-07-02 22:42:11.000000000 +0200
+++ linux-2.5.74/include/asm-arm26/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __ASM_ARM_DIV64
-#define __ASM_ARM_DIV64
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) \
-({ \
- int __res; \
- __res = ((unsigned long)n) % (unsigned int)base; \
- n = ((unsigned long)n) / (unsigned int)base; \
- __res; \
-})
-
-#endif
-
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-cris/div64.h linux-2.5.74/include/asm-cris/div64.h
--- linux-2.5.74.orig/include/asm-cris/div64.h 2003-07-02 22:42:06.000000000 +0200
+++ linux-2.5.74/include/asm-cris/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,16 +1 @@
-#ifndef __ASM_CRIS_DIV64
-#define __ASM_CRIS_DIV64
-
-/* copy from asm-arm */
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) \
-({ \
- int __res; \
- __res = ((unsigned long)n) % (unsigned int)base; \
- n = ((unsigned long)n) / (unsigned int)base; \
- __res; \
-})
-
-#endif
-
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-h8300/div64.h linux-2.5.74/include/asm-h8300/div64.h
--- linux-2.5.74.orig/include/asm-h8300/div64.h 2003-07-02 22:39:25.000000000 +0200
+++ linux-2.5.74/include/asm-h8300/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,13 +1 @@
-#ifndef H8300_DIV64_H
-#define H8300_DIV64_H
-
-/* n = n / base; return rem; */
-
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; \
-})
-
-#endif /* _H8300_DIV64_H */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-ia64/div64.h linux-2.5.74/include/asm-ia64/div64.h
--- linux-2.5.74.orig/include/asm-ia64/div64.h 2003-07-02 22:58:14.000000000 +0200
+++ linux-2.5.74/include/asm-ia64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,20 +1 @@
-#ifndef _ASM_IA64_DIV64_H
-#define _ASM_IA64_DIV64_H
-
-/*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <[email protected]>
- *
- * vsprintf uses this to divide a 64-bit integer N by a small integer BASE.
- * This is incredibly hard on IA-64...
- */
-
-#define do_div(n,base) \
-({ \
- int _res; \
- _res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- _res; \
-})
-
-#endif /* _ASM_IA64_DIV64_H */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-m68k/div64.h linux-2.5.74/include/asm-m68k/div64.h
--- linux-2.5.74.orig/include/asm-m68k/div64.h 2003-07-02 22:55:53.000000000 +0200
+++ linux-2.5.74/include/asm-m68k/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -3,7 +3,6 @@

/* n = n / base; return rem; */

-#if 1
#define do_div(n, base) ({ \
union { \
unsigned long n32[2]; \
@@ -23,13 +22,5 @@
(n) = __n.n64; \
__rem; \
})
-#else
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; \
-})
-#endif

#endif /* _M68K_DIV64_H */
diff -Nru linux-2.5.74.orig/include/asm-m68knommu/div64.h linux-2.5.74/include/asm-m68knommu/div64.h
--- linux-2.5.74.orig/include/asm-m68knommu/div64.h 2003-07-03 10:40:35.000000000 +0200
+++ linux-2.5.74/include/asm-m68knommu/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,13 +1 @@
-#ifndef _M68KNOMMU_DIV64_H
-#define _M68KNOMMU_DIV64_H
-
-/* n = n / base; return rem; */
-
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; \
-})
-
-#endif /* _M68K_DIV64_H */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-mips64/div64.h linux-2.5.74/include/asm-mips64/div64.h
--- linux-2.5.74.orig/include/asm-mips64/div64.h 2003-07-02 22:47:26.000000000 +0200
+++ linux-2.5.74/include/asm-mips64/div64.h 2003-07-03 09:39:28.000000000 +0200
@@ -27,23 +27,6 @@
(res) = __quot; \
__mod; })

-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n, base) ({ \
- unsigned long __quot; \
- unsigned int __mod; \
- unsigned long __div; \
- unsigned int __base; \
- \
- __div = (n); \
- __base = (base); \
- \
- __mod = __div % __base; \
- __quot = __div / __base; \
- \
- (n) = __quot; \
- __mod; })
+#include <asm-generic.h>

#endif /* _ASM_DIV64_H */
diff -Nru linux-2.5.74.orig/include/asm-parisc/div64.h linux-2.5.74/include/asm-parisc/div64.h
--- linux-2.5.74.orig/include/asm-parisc/div64.h 2003-07-02 22:48:40.000000000 +0200
+++ linux-2.5.74/include/asm-parisc/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,54 +1 @@
-#ifndef __ASM_PARISC_DIV64
-#define __ASM_PARISC_DIV64
-
-#ifdef __LP64__
-
-/*
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <[email protected]>
- *
- * vsprintf uses this to divide a 64-bit integer N by a small integer BASE.
- * This is incredibly hard on IA-64 and HPPA
- */
-
-#define do_div(n,base) \
-({ \
- int _res; \
- _res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- _res; \
-})
-
-#else
-/*
- * unsigned long long division. Yuck Yuck! What is Linux coming to?
- * This is 100% disgusting
- */
-#define do_div(n,base) \
-({ \
- unsigned long __low, __low2, __high, __rem; \
- __low = (n) & 0xffffffff; \
- __high = (n) >> 32; \
- if (__high) { \
- __rem = __high % (unsigned long)base; \
- __high = __high / (unsigned long)base; \
- __low2 = __low >> 16; \
- __low2 += __rem << 16; \
- __rem = __low2 % (unsigned long)base; \
- __low2 = __low2 / (unsigned long)base; \
- __low = __low & 0xffff; \
- __low += __rem << 16; \
- __rem = __low % (unsigned long)base; \
- __low = __low / (unsigned long)base; \
- n = __low + ((long long)__low2 << 16) + \
- ((long long) __high << 32); \
- } else { \
- __rem = __low % (unsigned long)base; \
- n = (__low / (unsigned long)base); \
- } \
- __rem; \
-})
-#endif
-
-#endif
-
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-ppc/div64.h linux-2.5.74/include/asm-ppc/div64.h
--- linux-2.5.74.orig/include/asm-ppc/div64.h 2003-07-02 22:57:06.000000000 +0200
+++ linux-2.5.74/include/asm-ppc/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,23 +1 @@
-#ifndef __PPC_DIV64
-#define __PPC_DIV64
-
-#include <linux/types.h>
-
-extern u32 __div64_32(u64 *dividend, u32 div);
-
-#define do_div(n, div) ({ \
- u64 __n = (n); \
- u32 __d = (div); \
- u32 __q, __r; \
- if ((__n >> 32) == 0) { \
- __q = (u32)__n / __d; \
- __r = (u32)__n - __q * __d; \
- (n) = __q; \
- } else { \
- __r = __div64_32(&__n, __d); \
- (n) = __n; \
- } \
- __r; \
-})
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-ppc64/div64.h linux-2.5.74/include/asm-ppc64/div64.h
--- linux-2.5.74.orig/include/asm-ppc64/div64.h 2003-07-02 22:39:34.000000000 +0200
+++ linux-2.5.74/include/asm-ppc64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,18 +1 @@
-#ifndef __PPC_DIV64
-#define __PPC_DIV64
-
-/* Copyright 2001 PPC64 Team, IBM Corp
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- __res; })
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-s390/div64.h linux-2.5.74/include/asm-s390/div64.h
--- linux-2.5.74.orig/include/asm-s390/div64.h 2003-07-02 22:50:17.000000000 +0200
+++ linux-2.5.74/include/asm-s390/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -43,13 +43,7 @@
})

#else /* __s390x__ */
-
-#define do_div(n,base) ({ \
-int __res; \
-__res = ((unsigned long) n) % (unsigned) base; \
-n = ((unsigned long) n) / (unsigned) base; \
-__res; })
-
+#include <asm-generic/div64.h>
#endif /* __s390x__ */

#endif
diff -Nru linux-2.5.74.orig/include/asm-sh/div64.h linux-2.5.74/include/asm-sh/div64.h
--- linux-2.5.74.orig/include/asm-sh/div64.h 2003-07-02 22:39:36.000000000 +0200
+++ linux-2.5.74/include/asm-sh/div64.h 2003-07-03 09:38:59.000000000 +0200
@@ -1,20 +1 @@
-#ifndef __ASM_SH_DIV64
-#define __ASM_SH_DIV64
-
-extern u64 __div64_32(u64 n, u32 d);
-
-#define do_div(n,base) ({ \
-u64 __n = (n), __q; \
-u32 __base = (base); \
-u32 __res; \
-if ((__n >> 32) == 0) { \
- __res = ((unsigned long) __n) % (unsigned) __base; \
- (n) = ((unsigned long) __n) / (unsigned) __base; \
-} else { \
- __q = __div64_32(__n, __base); \
- __res = __n - __q * __base; \
- (n) = __q; \
-} \
-__res; })
-
-#endif /* __ASM_SH_DIV64 */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-sparc/div64.h linux-2.5.74/include/asm-sparc/div64.h
--- linux-2.5.74.orig/include/asm-sparc/div64.h 2003-07-02 22:51:01.000000000 +0200
+++ linux-2.5.74/include/asm-sparc/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,11 +1 @@
-#ifndef __SPARC_DIV64
-#define __SPARC_DIV64
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
-
-#endif /* __SPARC_DIV64 */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-sparc64/div64.h linux-2.5.74/include/asm-sparc64/div64.h
--- linux-2.5.74.orig/include/asm-sparc64/div64.h 2003-07-02 22:46:06.000000000 +0200
+++ linux-2.5.74/include/asm-sparc64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __SPARC64_DIV64
-#define __SPARC64_DIV64
-
-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
-
-#endif /* __SPARC64_DIV64 */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-v850/div64.h linux-2.5.74/include/asm-v850/div64.h
--- linux-2.5.74.orig/include/asm-v850/div64.h 2003-07-02 22:53:46.000000000 +0200
+++ linux-2.5.74/include/asm-v850/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,11 +1 @@
-#ifndef __V850_DIV64_H__
-#define __V850_DIV64_H__
-
-/* We're not 64-bit, but... */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
-
-#endif /* __V850_DIV64_H__ */
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/include/asm-x86_64/div64.h linux-2.5.74/include/asm-x86_64/div64.h
--- linux-2.5.74.orig/include/asm-x86_64/div64.h 2003-07-02 22:54:30.000000000 +0200
+++ linux-2.5.74/include/asm-x86_64/div64.h 2003-07-03 09:37:22.000000000 +0200
@@ -1,14 +1 @@
-#ifndef __X86_64_DIV64
-#define __X86_64_DIV64
-
-/*
- * Hey, we're already 64-bit, no
- * need to play games..
- */
-#define do_div(n,base) ({ \
- int __res; \
- __res = ((unsigned long) (n)) % (unsigned) (base); \
- (n) = ((unsigned long) (n)) / (unsigned) (base); \
- __res; })
-
-#endif
+#include <asm-generic/div64.h>
diff -Nru linux-2.5.74.orig/lib/Makefile linux-2.5.74/lib/Makefile
--- linux-2.5.74.orig/lib/Makefile 2003-07-02 22:40:29.000000000 +0200
+++ linux-2.5.74/lib/Makefile 2003-07-03 09:37:22.000000000 +0200
@@ -5,7 +5,7 @@

lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
- kobject.o idr.o
+ kobject.o idr.o div64.o

lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o

--
// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

Please don't send Word attachments - http://www.gnu.org/philosophy/no-word-attachments.html

2003-07-08 18:23:39

by Andrew Morton

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

Bernardo Innocenti <[email protected]> wrote:
>
> Andrew, would you like to pick this patch up for me and forward it
> to Linus after it received some testing in -mm?

It got merged ages ago ;) Linus simply removed the pure thing.

2003-07-08 20:42:25

by Bernardo Innocenti

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Tuesday 08 July 2003 20:31, Andrew Morton wrote:
> Bernardo Innocenti <[email protected]> wrote:
> > Andrew, would you like to pick this patch up for me and forward it
> > to Linus after it received some testing in -mm?
>
> It got merged ages ago ;) Linus simply removed the pure thing.

Oops. He didn't remove it, he just picked up my older version
which also missed an important bug fix.

Here's an incremental patch against 2.5.74-bk4. Please apply.

---------------------------------------------------------------------

Fix problem introduced by previous do_div() patch:

- export the __div64_32 symbol for modules;

- add likely() to the fast path (divisor>>32 == 0);

- add __attribute__((pure)) to __div64_32() prototype so
the compiler knows global memory isn't clobbered;

- avoid building __div64_32() on 64bit architectures.


diff -Nru linux-2.5.74-bk4.orig/include/asm-generic/div64.h linux-2.5.74-bk4/include/asm-generic/div64.h
--- linux-2.5.74-bk4.orig/include/asm-generic/div64.h 2003-07-08 22:29:32.000000000 +0200
+++ linux-2.5.74-bk4/include/asm-generic/div64.h 2003-07-08 22:45:21.000000000 +0200
@@ -18,6 +18,7 @@
*/

#include <linux/types.h>
+#include <linux/compiler.h>

#if BITS_PER_LONG == 64

@@ -31,12 +32,12 @@

#elif BITS_PER_LONG == 32

-extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
+extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor) __attribute_pure__;

# define do_div(n,base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
- if (((n) >> 32) == 0) { \
+ if (likely(((n) >> 32) == 0)) { \
__rem = (uint32_t)(n) % __base; \
(n) = (uint32_t)(n) / __base; \
} else \
diff -Nru linux-2.5.74-bk4.orig/include/linux/compiler.h linux-2.5.74-bk4/include/linux/compiler.h
--- linux-2.5.74-bk4.orig/include/linux/compiler.h 2003-07-08 22:23:25.000000000 +0200
+++ linux-2.5.74-bk4/include/linux/compiler.h 2003-07-08 22:45:21.000000000 +0200
@@ -56,6 +56,24 @@
#define __attribute_used__ __attribute__((__unused__))
#endif

+/*
+ * From the GCC manual:
+ *
+ * Many functions have no effects except the return value and their
+ * return value depends only on the parameters and/or global
+ * variables. Such a function can be subject to common subexpression
+ * elimination and loop optimization just as an arithmetic operator
+ * would be.
+ * [...]
+ * The attribute `pure' is not implemented in GCC versions earlier
+ * than 2.96.
+ */
+#if (__GNUC__ == 2 && __GNUC_MINOR >= 96) || __GNUC__ > 2
+#define __attribute_pure__ __attribute__((pure))
+#else
+#define __attribute_pure__ /* unimplemented */
+#endif
+
/* This macro obfuscates arithmetic on a variable address so that gcc
shouldn't recognize the original var, and make assumptions about it */
#define RELOC_HIDE(ptr, off) \
diff -Nru linux-2.5.74-bk4.orig/lib/div64.c linux-2.5.74-bk4/lib/div64.c
--- linux-2.5.74-bk4.orig/lib/div64.c 2003-07-08 22:29:32.000000000 +0200
+++ linux-2.5.74-bk4/lib/div64.c 2003-07-08 22:45:21.000000000 +0200
@@ -12,13 +12,17 @@
* The fast case for (n>>32 == 0) is handled inline by do_div().
*
* Code generated for this function might be very inefficient
- * for some CPUs. div64_32() can be overridden by linking arch-specific
+ * for some CPUs. __div64_32() can be overridden by linking arch-specific
* assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
*/

#include <linux/types.h>
+#include <linux/module.h>
#include <asm/div64.h>

+/* Not needed on 64bit architectures */
+#if BITS_PER_LONG == 32
+
uint32_t __div64_32(uint64_t *n, uint32_t base)
{
uint32_t low, low2, high, rem;
@@ -43,3 +47,6 @@
return rem;
}

+EXPORT_SYMBOL(__div64_32);
+
+#endif /* BITS_PER_LONG == 32 */



--
// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

Please don't send Word attachments - http://www.gnu.org/philosophy/no-word-attachments.html


2003-07-10 15:27:59

by Richard Henderson

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Tue, Jul 08, 2003 at 08:27:26PM +0200, Bernardo Innocenti wrote:
> +extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor)
> __attribute_pure__;
...
> + __rem = __div64_32(&(n), __base); \

The pure declaration is very incorrect. You're writing to N.


r~

2003-07-10 16:04:37

by Andrea Arcangeli

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thu, Jul 10, 2003 at 08:40:19AM -0700, Richard Henderson wrote:
> On Tue, Jul 08, 2003 at 08:27:26PM +0200, Bernardo Innocenti wrote:
> > +extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor)
> > __attribute_pure__;
> ...
> > + __rem = __div64_32(&(n), __base); \
>
> The pure declaration is very incorrect. You're writing to N.

now pure sounds more reasonable, I wondered how could gcc keep track of
the stuff pointed by the parameters (especially if this stuff points to
other stuff etc.. ;). So only the pointer passed as parameter can
change, not the memory pointed by the pointer as in this case.

Andrea

2003-07-10 16:25:13

by Richard Henderson

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thu, Jul 10, 2003 at 06:18:59PM +0200, Andrea Arcangeli wrote:
> On Thu, Jul 10, 2003 at 08:40:19AM -0700, Richard Henderson wrote:
> > On Tue, Jul 08, 2003 at 08:27:26PM +0200, Bernardo Innocenti wrote:
> > > +extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor)
> > > __attribute_pure__;
> > ...
> > > + __rem = __div64_32(&(n), __base); \
> >
> > The pure declaration is very incorrect. You're writing to N.
>
> now pure sounds more reasonable, I wondered how could gcc keep track of
> the stuff pointed by the parameters (especially if this stuff points to
> other stuff etc.. ;).

Bernardo mis-interpreted the documentation.

Define "local memory" as memory from the current stack frame.

Define "non-local memory" as anything else (including stack memory from
another function, or a different instantiation of the current function).

Any function can read/write local memory (since that is not visible to
anyone outside the function).

A "const" function cannot read or write to non-local memory. There are
further constraints on not returning abnormally or not returning at all
that I'll not go into now.

A "pure" function can read non-local memory, but cannot write to it.

We use those conditions to determine if two invocations of a function
can be collapsed or moved or removed.



r~

2003-07-10 19:17:38

by Bernardo Innocenti

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thursday 10 July 2003 18:39, Richard Henderson wrote:

> On Thu, Jul 10, 2003 at 06:18:59PM +0200, Andrea Arcangeli wrote:
> > On Thu, Jul 10, 2003 at 08:40:19AM -0700, Richard Henderson wrote:
> > > On Tue, Jul 08, 2003 at 08:27:26PM +0200, Bernardo Innocenti wrote:
> > > > +extern uint32_t __div64_32(uint64_t *dividend, uint32_t
> > > > divisor) __attribute_pure__;
> > >
> > > ...
> > >
> > > > + __rem = __div64_32(&(n), __base); \
> > >
> > > The pure declaration is very incorrect. You're writing to N.
> >
> > now pure sounds more reasonable, I wondered how could gcc keep track
> > of the stuff pointed by the parameters (especially if this stuff
> > points to other stuff etc.. ;).

The compiler could easily tell what memory can be clobbered by a pointer
by applying type-based aliasing rules. For example, a function taking a
"char *" can't clobber memory objects declared as "long bar" or
"struct foo".

Without type based alias analysis, the compiler is forced to flush
all registers containing copies of memory objects before function
call and reloading values from memory afterwards.


> Bernardo mis-interpreted the documentation. [...]

I'm afraid you're right. Here's a code snippet from gcc/calls.c that
shows what the compiler _really_ does for pure calls:

/* If the result of a pure or const function call is ignored (or void),
and none of its arguments are volatile, we can avoid expanding the
call and just evaluate the arguments for side-effects. */
if ((flags & (ECF_CONST | ECF_PURE))
&& (ignore || target == const0_rtx
|| TYPE_MODE (TREE_TYPE (exp)) == VOIDmode))
{
bool volatilep = false;
tree arg;

for (arg = actparms; arg; arg = TREE_CHAIN (arg))
if (TREE_THIS_VOLATILE (TREE_VALUE (arg)))
{
volatilep = true;
break;
}

if (! volatilep)
{
for (arg = actparms; arg; arg = TREE_CHAIN (arg))
expand_expr (TREE_VALUE (arg), const0_rtx,
VOIDmode, EXPAND_NORMAL);
return const0_rtx;
}
}


Therefore this optimization is to be undone. Would it work if
we could use references instead of pointers? I think it
wouldn't. A new attribute would be needed for this case.

Just to open some interesting speculation, do you think we'd
get better code by just getting rid of __attribute__((pure))
or by changing __do_div64() to do something like this?

typedef struct { uint64_t quot, uint32_t rem } __quotrem64;
__quotrem64 __do_div64(uint64_t div, uint32_t base) __attribute__((const));

#define do_div(n,base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
if (likely(((n) >> 32) == 0)) { \
__rem = (uint32_t)(n) % __base; \
(n) = (uint32_t)(n) / __base; \
} else { \
__quotrem64 __qr = __div64_32((n), __base); \
(n) = __qr.quot; \
__rem = __qr.rem; \
} \
__rem; \
})

Boy, that's ugly! It's too bad C can't do it the Perl way:

(n,rem) = __div64_32(n, base);

--
// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

Please don't send Word attachments - http://www.gnu.org/philosophy/no-word-attachments.html


2003-07-10 19:40:09

by Dale Johannesen

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thursday, July 10, 2003, at 12:31 PM, Bernardo Innocenti wrote:
>
> The compiler could easily tell what memory can be clobbered by a
> pointer
> by applying type-based aliasing rules. For example, a function taking a
> "char *" can't clobber memory objects declared as "long bar" or
> "struct foo".

No, for two reasons. First, anything can be aliased by a char. Second,
the restriction is on the type of the eventual memory reference,
not on the type of the pointer. Assuming an int* p, you can still do
*((char*)p) and
that may alias anything. So you must check each dereference; this
can't be
done easily at function entry.

2003-07-10 20:05:25

by Andrea Arcangeli

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thu, Jul 10, 2003 at 09:31:45PM +0200, Bernardo Innocenti wrote:
> On Thursday 10 July 2003 18:39, Richard Henderson wrote:
>
> > On Thu, Jul 10, 2003 at 06:18:59PM +0200, Andrea Arcangeli wrote:
> > > On Thu, Jul 10, 2003 at 08:40:19AM -0700, Richard Henderson wrote:
> > > > On Tue, Jul 08, 2003 at 08:27:26PM +0200, Bernardo Innocenti wrote:
> > > > > +extern uint32_t __div64_32(uint64_t *dividend, uint32_t
> > > > > divisor) __attribute_pure__;
> > > >
> > > > ...
> > > >
> > > > > + __rem = __div64_32(&(n), __base); \
> > > >
> > > > The pure declaration is very incorrect. You're writing to N.
> > >
> > > now pure sounds more reasonable, I wondered how could gcc keep track
> > > of the stuff pointed by the parameters (especially if this stuff
> > > points to other stuff etc.. ;).
>
> The compiler could easily tell what memory can be clobbered by a pointer
> by applying type-based aliasing rules. For example, a function taking a
> "char *" can't clobber memory objects declared as "long bar" or
> "struct foo".
>
> Without type based alias analysis, the compiler is forced to flush
> all registers containing copies of memory objects before function
> call and reloading values from memory afterwards.

the kernel isn't complaint with the alias analysis, that's why it has to
be turned off (-fnostrict-aliasing) or stuff would break.

> Boy, that's ugly! It's too bad C can't do it the Perl way:
>
> (n,rem) = __div64_32(n, base);

or the python way:

n, rem = __div64_32(n, base)

;)

Andrea

2003-07-10 21:51:59

by Richard Henderson

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thu, Jul 10, 2003 at 09:31:45PM +0200, Bernardo Innocenti wrote:
> Just to open some interesting speculation, do you think we'd
> get better code by just getting rid of __attribute__((pure))
> or by changing __do_div64() to do something like this?
>
> typedef struct { uint64_t quot, uint32_t rem } __quotrem64;
> __quotrem64 __do_div64(uint64_t div, uint32_t base) __attribute__((const));

No. Most targets require structures be returned in memory.

If people really care beyond the generic, they'll write
special assembly for it.


r~

2003-07-10 22:45:54

by Bernardo Innocenti

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thursday 10 July 2003 22:19, Andrea Arcangeli wrote:

> > Without type based alias analysis, the compiler is forced to flush
> > all registers containing copies of memory objects before function
> > call and reloading values from memory afterwards.
>
> the kernel isn't complaint with the alias analysis, that's why it has
> to be turned off (-fnostrict-aliasing) or stuff would break.

Yeah, I noticed. Writing low-level code without breaking strict aliasing
rules can be quite difficult if you don't want to give up any of your clever
tricks. I have a set of macros for handling linked lists that I can't get to
compile without those damn alias warnings with the latest GCC versions.

Besides, pushing our philosophycal discussion forward, what the generic
do_div() is doing might be vary bad for performance. After extracting the
address of n, the compiler must conservatively assume that any following
pointer dereference or function call could alter the contents of n.

Since strict aliasing is turned off, access to the divisor will become
slow as hell. Almost as if it was declared volatile.

Ok, as Richard said, it's just the generic version for those who don't
care enough to write their own optimized version.

> > Boy, that's ugly! It's too bad C can't do it the Perl way:
> >
> > (n,rem) = __div64_32(n, base);
>
> or the python way:
>
> n, rem = __div64_32(n, base)
>
> ;)

You beat me!

--
// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

Please don't send Word attachments - http://www.gnu.org/philosophy/no-word-attachments.html


2003-07-10 22:58:53

by Bernardo Innocenti

[permalink] [raw]
Subject: Re: [PATCH] Fix do_div() for all architectures

On Thursday 10 July 2003 17:40, Richard Henderson wrote:
> On Tue, Jul 08, 2003 at 08:27:26PM +0200, Bernardo Innocenti wrote:
> > +extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor)
> > __attribute_pure__;
>
> ...
>
> > + __rem = __div64_32(&(n), __base); \
>
> The pure declaration is very incorrect. You're writing to N.

Here comes the obvious fix. Mea culpa, mea culpa, mea maxima culpa!

NOTE: I've intentionally left the __attribute_pure__ definition in
linux/compiler.h since it might apply to many other functions.


Linus, please apply and forgive me for getting this simple patch wrong
so many times in a row.

-------------------------------------------------------------------------

- remove incorrect __attribute_pure__ from __div64_32() since it obviously
clobbers memory through &(n);


diff -Nru linux-2.5.74-bk4.orig/include/asm-generic/div64.h linux-2.5.74-bk4/include/asm-generic/div64.h
--- linux-2.5.74-bk4.orig/include/asm-generic/div64.h 2003-07-11 01:00:44.000000000 +0200
+++ linux-2.5.74-bk4/include/asm-generic/div64.h 2003-07-11 00:59:52.000000000 +0200
@@ -32,7 +32,7 @@

#elif BITS_PER_LONG == 32

-extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor) __attribute_pure__;
+extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);

# define do_div(n,base) ({ \
uint32_t __base = (base); \

--
// Bernardo Innocenti - Develer S.r.l., R&D dept.
\X/ http://www.develer.com/

Please don't send Word attachments - http://www.gnu.org/philosophy/no-word-attachments.html