diff mbox

[7/9] arm: replace arbitrary divisions

Message ID 1381767815-12510-8-git-send-email-drjones@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrew Jones Oct. 14, 2013, 4:23 p.m. UTC
arm can't do arbitrary divisions without software support. Usually
libgcc would jump in here, but depending on the toolchain used that may
or may not work. Luckily, we only care about two types of divisions.
Divide by 10 and divide by 16. Divide by 16 is already covered by gcc
since it's a power of two. Divide by 10 can be hacked up using a
multiplication and shift.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 lib/divmod.h | 20 ++++++++++++++++++++
 lib/printf.c | 27 ++++++++++++++-------------
 2 files changed, 34 insertions(+), 13 deletions(-)
 create mode 100644 lib/divmod.h

Comments

Christoffer Dall Oct. 17, 2013, 1:06 a.m. UTC | #1
On Mon, Oct 14, 2013 at 06:23:33PM +0200, Andrew Jones wrote:
> arm can't do arbitrary divisions without software support. Usually
> libgcc would jump in here, but depending on the toolchain used that may
> or may not work. Luckily, we only care about two types of divisions.
> Divide by 10 and divide by 16. Divide by 16 is already covered by gcc
> since it's a power of two. Divide by 10 can be hacked up using a
> multiplication and shift.

Isn't this just a matter of supplying a few libc implementations to
handle div_by_0 and that sort?  I'm pretty sure we had that working in
the kvm-selftest for ARM thingy that allowed you to use the standard '/'
operator in C code.... I suspect there will be more users of this
eventually.

Or wait, do you mean 'long long' operations by arbitrary? In that case,
I'm less sure...  A library implementation to support the operator would
still be preferred IMHO.

> 
> Signed-off-by: Andrew Jones <drjones@redhat.com>
> ---
>  lib/divmod.h | 20 ++++++++++++++++++++
>  lib/printf.c | 27 ++++++++++++++-------------
>  2 files changed, 34 insertions(+), 13 deletions(-)
>  create mode 100644 lib/divmod.h
> 
> diff --git a/lib/divmod.h b/lib/divmod.h
> new file mode 100644
> index 0000000000000..c0f04d7d8386d
> --- /dev/null
> +++ b/lib/divmod.h
> @@ -0,0 +1,20 @@
> +#ifndef _DIVMOD_H_
> +#define _DIVMOD_H_
> +#ifdef __arm__
> +static inline long long div10(long long n)
> +{
> +    /*
> +     * multiply n by 2^32/10 and the result of n/10
> +     * will be in the upper word
> +     */
> +    return (n * 0x1999999aU) >> 32;
> +}
> +static inline int mod10(long long n)
> +{
> +    return n - div10(n) * 10;
> +}
> +#else
> +#define div10(n) ((n) / 10)
> +#define mod10(n) ((n) % 10)
> +#endif
> +#endif
> diff --git a/lib/printf.c b/lib/printf.c
> index 867eb1926f742..9fb8133868c7a 100644
> --- a/lib/printf.c
> +++ b/lib/printf.c
> @@ -1,4 +1,5 @@
>  #include "libcflat.h"
> +#include "divmod.h"
>  
>  typedef struct pstream {
>      char *buffer;
> @@ -23,7 +24,7 @@ void print_str(pstream_t *p, const char *s)
>  
>  static char digits[16] = "0123456789abcdef";
>  
> -void print_int(pstream_t *ps, long long n, int base)
> +void print_int(pstream_t *ps, long long n)
>  {
>      char buf[sizeof(long) * 3 + 2], *p = buf;
>      int s = 0, i;
> @@ -34,8 +35,8 @@ void print_int(pstream_t *ps, long long n, int base)
>      }
>  
>      while (n) {
> -	*p++ = digits[n % base];
> -	n /= base;
> +	*p++ = digits[mod10(n)];
> +	n = div10(n);
>      }
>  
>      if (s)
> @@ -57,14 +58,14 @@ void print_int(pstream_t *ps, long long n, int base)
>      print_str(ps, buf);
>  }
>  
> -void print_unsigned(pstream_t *ps, unsigned long long n, int base)
> +void print_unsigned(pstream_t *ps, unsigned long long n)
>  {
>      char buf[sizeof(long) * 3 + 1], *p = buf;
>      int i;
>  
>      while (n) {
> -	*p++ = digits[n % base];
> -	n /= base;
> +	*p++ = digits[n % 16];
> +	n /= 16;
>      }
>  
>      if (p == buf)
> @@ -116,32 +117,32 @@ int vsnprintf(char *buf, int size, const char *fmt, va_list va)
>  	case 'd':
>  	    switch (nlong) {
>  	    case 0:
> -		print_int(&s, va_arg(va, int), 10);
> +		print_int(&s, va_arg(va, int));
>  		break;
>  	    case 1:
> -		print_int(&s, va_arg(va, long), 10);
> +		print_int(&s, va_arg(va, long));
>  		break;
>  	    default:
> -		print_int(&s, va_arg(va, long long), 10);
> +		print_int(&s, va_arg(va, long long));
>  		break;
>  	    }
>  	    break;
>  	case 'x':
>  	    switch (nlong) {
>  	    case 0:
> -		print_unsigned(&s, va_arg(va, unsigned), 16);
> +		print_unsigned(&s, va_arg(va, unsigned));
>  		break;
>  	    case 1:
> -		print_unsigned(&s, va_arg(va, unsigned long), 16);
> +		print_unsigned(&s, va_arg(va, unsigned long));
>  		break;
>  	    default:
> -		print_unsigned(&s, va_arg(va, unsigned long long), 16);
> +		print_unsigned(&s, va_arg(va, unsigned long long));
>  		break;
>  	    }
>  	    break;
>  	case 'p':
>  	    print_str(&s, "0x");
> -	    print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
> +	    print_unsigned(&s, (unsigned long)va_arg(va, void *));
>  	    break;
>  	case 's':
>  	    print_str(&s, va_arg(va, const char *));
> -- 
> 1.8.1.4
> 
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andrew Jones Oct. 17, 2013, 10:03 a.m. UTC | #2
On Wed, Oct 16, 2013 at 06:06:29PM -0700, Christoffer Dall wrote:
> On Mon, Oct 14, 2013 at 06:23:33PM +0200, Andrew Jones wrote:
> > arm can't do arbitrary divisions without software support. Usually
> > libgcc would jump in here, but depending on the toolchain used that may
> > or may not work. Luckily, we only care about two types of divisions.
> > Divide by 10 and divide by 16. Divide by 16 is already covered by gcc
> > since it's a power of two. Divide by 10 can be hacked up using a
> > multiplication and shift.
> 
> Isn't this just a matter of supplying a few libc implementations to
> handle div_by_0 and that sort?  I'm pretty sure we had that working in
> the kvm-selftest for ARM thingy that allowed you to use the standard '/'
> operator in C code.... I suspect there will be more users of this
> eventually.
> 
> Or wait, do you mean 'long long' operations by arbitrary? In that case,
> I'm less sure...  A library implementation to support the operator would
> still be preferred IMHO.

No, that's not what I meant, nor can I pretend that's what I meant now :-)
I saw that you had brought lib1funcs.S into kvm-selftest, but I'm not sure
why I rejected doing the same thing... Probably because my reflex was to
not adopt a bunch of assembler that I didn't want to maintain. But I guess
there's nothing to maintain there. It works, and will always work. I'll
switch to using it.

drew
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christoffer Dall Oct. 17, 2013, 6:59 p.m. UTC | #3
On Thu, Oct 17, 2013 at 12:03:23PM +0200, Andrew Jones wrote:
> On Wed, Oct 16, 2013 at 06:06:29PM -0700, Christoffer Dall wrote:
> > On Mon, Oct 14, 2013 at 06:23:33PM +0200, Andrew Jones wrote:
> > > arm can't do arbitrary divisions without software support. Usually
> > > libgcc would jump in here, but depending on the toolchain used that may
> > > or may not work. Luckily, we only care about two types of divisions.
> > > Divide by 10 and divide by 16. Divide by 16 is already covered by gcc
> > > since it's a power of two. Divide by 10 can be hacked up using a
> > > multiplication and shift.
> > 
> > Isn't this just a matter of supplying a few libc implementations to
> > handle div_by_0 and that sort?  I'm pretty sure we had that working in
> > the kvm-selftest for ARM thingy that allowed you to use the standard '/'
> > operator in C code.... I suspect there will be more users of this
> > eventually.
> > 
> > Or wait, do you mean 'long long' operations by arbitrary? In that case,
> > I'm less sure...  A library implementation to support the operator would
> > still be preferred IMHO.
> 
> No, that's not what I meant, nor can I pretend that's what I meant now :-)
> I saw that you had brought lib1funcs.S into kvm-selftest, but I'm not sure
> why I rejected doing the same thing... Probably because my reflex was to
> not adopt a bunch of assembler that I didn't want to maintain. But I guess
> there's nothing to maintain there. It works, and will always work. I'll
> switch to using it.
> 
I think so, and I'll gladly help you maintain those bits.

-Christoffer
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/lib/divmod.h b/lib/divmod.h
new file mode 100644
index 0000000000000..c0f04d7d8386d
--- /dev/null
+++ b/lib/divmod.h
@@ -0,0 +1,20 @@ 
+#ifndef _DIVMOD_H_
+#define _DIVMOD_H_
+#ifdef __arm__
+static inline long long div10(long long n)
+{
+    /*
+     * multiply n by 2^32/10 and the result of n/10
+     * will be in the upper word
+     */
+    return (n * 0x1999999aU) >> 32;
+}
+static inline int mod10(long long n)
+{
+    return n - div10(n) * 10;
+}
+#else
+#define div10(n) ((n) / 10)
+#define mod10(n) ((n) % 10)
+#endif
+#endif
diff --git a/lib/printf.c b/lib/printf.c
index 867eb1926f742..9fb8133868c7a 100644
--- a/lib/printf.c
+++ b/lib/printf.c
@@ -1,4 +1,5 @@ 
 #include "libcflat.h"
+#include "divmod.h"
 
 typedef struct pstream {
     char *buffer;
@@ -23,7 +24,7 @@  void print_str(pstream_t *p, const char *s)
 
 static char digits[16] = "0123456789abcdef";
 
-void print_int(pstream_t *ps, long long n, int base)
+void print_int(pstream_t *ps, long long n)
 {
     char buf[sizeof(long) * 3 + 2], *p = buf;
     int s = 0, i;
@@ -34,8 +35,8 @@  void print_int(pstream_t *ps, long long n, int base)
     }
 
     while (n) {
-	*p++ = digits[n % base];
-	n /= base;
+	*p++ = digits[mod10(n)];
+	n = div10(n);
     }
 
     if (s)
@@ -57,14 +58,14 @@  void print_int(pstream_t *ps, long long n, int base)
     print_str(ps, buf);
 }
 
-void print_unsigned(pstream_t *ps, unsigned long long n, int base)
+void print_unsigned(pstream_t *ps, unsigned long long n)
 {
     char buf[sizeof(long) * 3 + 1], *p = buf;
     int i;
 
     while (n) {
-	*p++ = digits[n % base];
-	n /= base;
+	*p++ = digits[n % 16];
+	n /= 16;
     }
 
     if (p == buf)
@@ -116,32 +117,32 @@  int vsnprintf(char *buf, int size, const char *fmt, va_list va)
 	case 'd':
 	    switch (nlong) {
 	    case 0:
-		print_int(&s, va_arg(va, int), 10);
+		print_int(&s, va_arg(va, int));
 		break;
 	    case 1:
-		print_int(&s, va_arg(va, long), 10);
+		print_int(&s, va_arg(va, long));
 		break;
 	    default:
-		print_int(&s, va_arg(va, long long), 10);
+		print_int(&s, va_arg(va, long long));
 		break;
 	    }
 	    break;
 	case 'x':
 	    switch (nlong) {
 	    case 0:
-		print_unsigned(&s, va_arg(va, unsigned), 16);
+		print_unsigned(&s, va_arg(va, unsigned));
 		break;
 	    case 1:
-		print_unsigned(&s, va_arg(va, unsigned long), 16);
+		print_unsigned(&s, va_arg(va, unsigned long));
 		break;
 	    default:
-		print_unsigned(&s, va_arg(va, unsigned long long), 16);
+		print_unsigned(&s, va_arg(va, unsigned long long));
 		break;
 	    }
 	    break;
 	case 'p':
 	    print_str(&s, "0x");
-	    print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
+	    print_unsigned(&s, (unsigned long)va_arg(va, void *));
 	    break;
 	case 's':
 	    print_str(&s, va_arg(va, const char *));