Supply signed division to fix ARM compilation.

Previously we supplied only unsigned divisions on platforms that need software
division.
Yet compiler may itself use a signed division. A typical example would be a
difference between 2 pointers which involves division by object size.
This commit is contained in:
Vladimir Serbinenko 2015-02-22 17:24:28 +01:00
parent e1d4520bfb
commit f034fab620
6 changed files with 198 additions and 51 deletions

View file

@ -159,6 +159,8 @@ kernel = {
terminfoinkernel = commands/extcmd.c;
terminfoinkernel = lib/arg.c;
softdiv = lib/division.c;
i386 = kern/i386/dl.c;
i386_xen = kern/i386/dl.c;
@ -1969,6 +1971,12 @@ module = {
enable = xen;
};
module = {
name = div;
common = lib/division.c;
enable = no_softdiv;
};
module = {
name = div_test;
common = tests/div_test.c;

View file

@ -58,15 +58,11 @@ FUNCTION(__aeabi_lmul)
ldmfd sp!, {r4, fp}
bx lr
.macro division parent
.macro division32 parent
sub sp, sp, #8 @ Allocate naturally aligned 64-bit space
stmfd sp!, {r3,lr} @ Dummy r3 to maintain stack alignment
add r3, sp, #8 @ Set r3 to address of 64-bit space
str r3, [sp] @ Stack parameter, pointer to 64-bit space
mov r2, r1
mov r1, #0
mov r3, #0
add r2, sp, #8 @ Set r2 to address of 64-bit space
bl \parent
ldr r1, [sp, #8] @ Extract remainder
ldmfd sp!, {r3,lr} @ Pop into an unused arg/scratch register
@ -75,8 +71,9 @@ FUNCTION(__aeabi_lmul)
.endm
FUNCTION(__aeabi_uidivmod)
division grub_divmod64
division32 grub_divmod32
FUNCTION(__aeabi_idivmod)
division32 grub_divmod32s
/*
* Null divide-by-zero handler

View file

@ -594,10 +594,10 @@ grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r)
grub_uint64_t m = 0;
/* ARM and IA64 don't have a fast 32-bit division.
Using that code would just make us use libgcc routines, calling
them twice (once for modulo and once for quotient.
Using that code would just make us use software division routines, calling
ourselves indirectly and hence getting infinite recursion.
*/
#if !defined (__arm__) && !defined (__ia64__)
#if !GRUB_DIVISION_IN_SOFTWARE
/* Skip the slow computation if 32-bit arithmetic is possible. */
if (n < 0xffffffff && d < 0xffffffff)
{
@ -633,7 +633,7 @@ grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r)
#if !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU)
#if defined (__arm__)
#if GRUB_DIVISION_IN_SOFTWARE
grub_uint32_t
__udivsi3 (grub_uint32_t a, grub_uint32_t b)
@ -641,6 +641,12 @@ __udivsi3 (grub_uint32_t a, grub_uint32_t b)
return grub_divmod64 (a, b, 0);
}
grub_int32_t
__divsi3 (grub_int32_t a, grub_int32_t b)
{
return grub_divmod64s (a, b, 0);
}
grub_uint32_t
__umodsi3 (grub_uint32_t a, grub_uint32_t b)
{
@ -649,6 +655,42 @@ __umodsi3 (grub_uint32_t a, grub_uint32_t b)
return ret;
}
grub_int32_t
__modsi3 (grub_int32_t a, grub_int32_t b)
{
grub_int64_t ret;
grub_divmod64s (a, b, &ret);
return ret;
}
grub_uint64_t
__udivdi3 (grub_uint64_t a, grub_uint64_t b)
{
return grub_divmod64 (a, b, 0);
}
grub_uint64_t
__umoddi3 (grub_uint64_t a, grub_uint64_t b)
{
grub_uint64_t ret;
grub_divmod64 (a, b, &ret);
return ret;
}
grub_int64_t
__divdi3 (grub_int64_t a, grub_int64_t b)
{
return grub_divmod64s (a, b, 0);
}
grub_int64_t
__moddi3 (grub_int64_t a, grub_int64_t b)
{
grub_int64_t ret;
grub_divmod64s (a, b, &ret);
return ret;
}
#endif
#ifdef NEED_CTZDI2
@ -735,30 +777,15 @@ __ctzsi2 (grub_uint32_t x)
grub_uint32_t
__aeabi_uidiv (grub_uint32_t a, grub_uint32_t b)
__attribute__ ((alias ("__udivsi3")));
grub_int32_t
__aeabi_idiv (grub_int32_t a, grub_int32_t b)
__attribute__ ((alias ("__divsi3")));
void *__aeabi_memcpy (void *dest, const void *src, grub_size_t n)
__attribute__ ((alias ("grub_memcpy")));
void *__aeabi_memset (void *s, int c, grub_size_t n)
__attribute__ ((alias ("grub_memset")));
#endif
#if defined (__ia64__)
grub_uint64_t
__udivdi3 (grub_uint64_t a, grub_uint64_t b)
{
return grub_divmod64 (a, b, 0);
}
grub_uint64_t
__umoddi3 (grub_uint64_t a, grub_uint64_t b)
{
grub_uint64_t ret;
grub_divmod64 (a, b, &ret);
return ret;
}
#endif
#endif /* GRUB_UTIL */
/* Convert a long long value to a string. This function avoids 64-bit

View file

@ -97,6 +97,72 @@ test64 (grub_uint64_t a, grub_uint64_t b)
#endif
}
static grub_int64_t
abs64(grub_int64_t a)
{
return a > 0 ? a : -a;
}
static void
test32s (grub_int32_t a, grub_int32_t b)
{
grub_int64_t q, r;
if (b == 0)
return;
q = grub_divmod64s (a, b, &r);
grub_test_assert (a > 0 ? r >= 0 : r <= 0, "remainder sign mismatch: %lld %% %lld = %lld",
(long long) a, (long long) b, (long long) r);
grub_test_assert (((a > 0) == (b > 0)) ? q >= 0 : q <= 0, "quotient sign mismatch: %lld / %lld = %lld",
(long long) a, (long long) b, (long long) q);
grub_test_assert (abs64(r) < abs64(b), "remainder is larger than dividend: %lld %% %lld = %lld",
(long long) a, (long long) b, (long long) r);
grub_test_assert (q * b + r == a, "division doesn't satisfy base property: %lld * %lld + %lld != %lld", (long long) q, (long long) b, (long long) r,
(long long) a);
if (0) { grub_test_assert (q == (a / b),
"C compiler division failure in 0x%llx, 0x%llx", (long long) a, (long long) b);
grub_test_assert (r == (a % b),
"C compiler modulo failure in 0x%llx, 0x%llx", (long long) a, (long long) b);
}
}
static void
test64s (grub_int64_t a, grub_int64_t b)
{
grub_int64_t q, r;
q = grub_divmod64s (a, b, &r);
grub_test_assert (a > 0 ? r >= 0 : r <= 0, "remainder sign mismatch: %lld %% %lld = %lld",
(long long) a, (long long) b, (long long) r);
grub_test_assert (((a > 0) == (b > 0)) ? q >= 0 : q <= 0, "quotient sign mismatch: %lld / %lld = %lld",
(long long) a, (long long) b, (long long) q);
grub_test_assert (abs64(r) < abs64(b), "remainder is larger than dividend: %lld %% %lld = %lld",
(long long) a, (long long) b, (long long) r);
grub_test_assert (q * b + r == a, "division doesn't satisfy base property: 0x%llx * 0x%llx + 0x%llx != 0x%llx", (long long) q, (long long) b, (long long) r,
(long long) a);
#if GRUB_TARGET_SIZEOF_VOID_P == 8
grub_test_assert (q == (a / b),
"C compiler division failure in 0x%llx, 0x%llx", (long long) a, (long long) b);
grub_test_assert (r == (a % b),
"C compiler modulo failure in 0x%llx, 0x%llx", (long long) a, (long long) b);
#endif
}
static void
test_all(grub_uint64_t a, grub_uint64_t b)
{
test64 (a, b);
test32 (a, b);
test64s (a, b);
test32s (a, b);
test64s (a, -b);
test32s (a, -b);
test64s (-a, b);
test32s (-a, b);
test64s (-a, -b);
test32s (-a, -b);
}
static void
div_test (void)
{
@ -105,8 +171,7 @@ div_test (void)
for (i = 0; i < ARRAY_SIZE (vectors); i++)
{
test64 (vectors[i][0], vectors[i][1]);
test32 (vectors[i][0], vectors[i][1]);
test_all (vectors[i][0], vectors[i][1]);
}
for (i = 0; i < 40000; i++)
{
@ -116,9 +181,7 @@ div_test (void)
b = 1;
if (a == 0)
a = 1;
test64 (a, b);
test32 (a, b);
test_all (a, b);
}
}