From 93b0dc92eab55f8729b4798b383d4670073ebddc Mon Sep 17 00:00:00 2001 From: Jorgen Lundman Date: Tue, 15 May 2012 05:45:09 +0000 Subject: [PATCH] Fix ARM 64-bit division Correctly implementating 64-bit division for ARM requires more than just providing the __aeabi_uldivmod() and __aeabi_ldivmod() symbols. They are need to be implemented is such a way that the quotient and remainder and left in specific registers after the division operation completes. This change updates the wrapper functions to accomplish this according to the official ARM Run-time ABI. Signed-off-by: Brian Behlendorf Closes zfsonlinux/zfs#706 --- module/spl/spl-generic.c | 51 +++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/module/spl/spl-generic.c b/module/spl/spl-generic.c index d22100ea3..785f668d6 100644 --- a/module/spl/spl-generic.c +++ b/module/spl/spl-generic.c @@ -214,22 +214,57 @@ EXPORT_SYMBOL(__umoddi3); #if defined(__arm) || defined(__arm__) /* - * Implementation of 64-bit unsigned division for 32-bit arm machines. + * Implementation of 64-bit (un)signed division for 32-bit arm machines. + * + * Run-time ABI for the ARM Architecture (page 20). A pair of (unsigned) + * long longs is returned in {{r0, r1}, {r2,r3}}, the quotient in {r0, r1}, + * and the remainder in {r2, r3}. The return type is specifically left + * set to 'void' to ensure the compiler does not overwrite these registers + * during the return. All results are in registers as per ABI */ -uint64_t +void __aeabi_uldivmod(uint64_t u, uint64_t v) { - return __udivdi3(u, v); + uint64_t res; + uint64_t mod; + + res = __udivdi3(u, v); + mod = __umoddi3(u, v); + { + register uint32_t r0 asm("r0") = (res & 0xFFFFFFFF); + register uint32_t r1 asm("r1") = (res >> 32); + register uint32_t r2 asm("r2") = (mod & 0xFFFFFFFF); + register uint32_t r3 asm("r3") = (mod >> 32); + + asm volatile("" + : "+r"(r0), "+r"(r1), "+r"(r2),"+r"(r3) /* output */ + : "r"(r0), "r"(r1), "r"(r2), "r"(r3)); /* input */ + + return; /* r0; */ + } } EXPORT_SYMBOL(__aeabi_uldivmod); -/* - * Implementation of 64-bit signed division for 32-bit arm machines. - */ -int64_t +void __aeabi_ldivmod(int64_t u, int64_t v) { - return __divdi3(u, v); + int64_t res; + uint64_t mod; + + res = __divdi3(u, v); + mod = __umoddi3(u, v); + { + register uint32_t r0 asm("r0") = (res & 0xFFFFFFFF); + register uint32_t r1 asm("r1") = (res >> 32); + register uint32_t r2 asm("r2") = (mod & 0xFFFFFFFF); + register uint32_t r3 asm("r3") = (mod >> 32); + + asm volatile("" + : "+r"(r0), "+r"(r1), "+r"(r2),"+r"(r3) /* output */ + : "r"(r0), "r"(r1), "r"(r2), "r"(r3)); /* input */ + + return; /* r0; */ + } } EXPORT_SYMBOL(__aeabi_ldivmod); #endif /* __arm || __arm__ */