diff options
author | Jorgen Lundman <[email protected]> | 2012-05-15 05:45:09 +0000 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2012-05-22 09:27:11 -0700 |
commit | 93b0dc92eab55f8729b4798b383d4670073ebddc (patch) | |
tree | dba682a2f151d4ed00a849c09d812150b18c39e4 | |
parent | 38d31a1e576fd7c6393b3039ad04b8be2ee95f81 (diff) |
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 <[email protected]>
Closes zfsonlinux/zfs#706
-rw-r--r-- | module/spl/spl-generic.c | 51 |
1 files 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__ */ |