summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJorgen Lundman <[email protected]>2012-05-15 05:45:09 +0000
committerBrian Behlendorf <[email protected]>2012-05-22 09:27:11 -0700
commit93b0dc92eab55f8729b4798b383d4670073ebddc (patch)
treedba682a2f151d4ed00a849c09d812150b18c39e4
parent38d31a1e576fd7c6393b3039ad04b8be2ee95f81 (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.c51
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__ */