On 32-bit PowerPC, GCC lowers '/' and '%' on unsigned 64-bit operands
to calls to __udivdi3() and __umoddi3(). U-Boot's private libgcc lacks
these routines, so turris_1x_sdcard fails to link with
CONFIG_USE_PRIVATE_LIBGCC=y:

  undefined reference to `__udivdi3'

Implement both with a copy of __div64_32() from lib/div64.c whose
divisor is widened to 64 bits. The copy keeps the private libgcc
independent of lib/, which not every xPL phase links.

Build the file without LTO, as ARM does for eabi_compat.o: LTO can
drop these functions before GCC generates the calls that need them.

Signed-off-by: Josef Schlehofer <[email protected]>
Cc: Tom Rini <[email protected]>
Cc: Marek BehĂșn <[email protected]>
---
Changes in v2:
- new patch

At -Os the two take 300 bytes, against 1036 and 992 bytes of
_udivdi3.o and _umoddi3.o in Debian's libgcc 14.2.
---
 arch/powerpc/lib/Makefile  |  3 +-
 arch/powerpc/lib/udivdi3.c | 82 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/lib/udivdi3.c

diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile
index 90f0e1e5fa9..23e62996701 100644
--- a/arch/powerpc/lib/Makefile
+++ b/arch/powerpc/lib/Makefile
@@ -6,7 +6,8 @@
 ## Build a couple of necessary functions into a private libgcc
 ## if the user asked for it
 lib-$(CONFIG_USE_PRIVATE_LIBGCC) += _ashldi3.o _ashrdi3.o _lshrdi3.o \
-                                   crtsavres.o
+                                   crtsavres.o udivdi3.o
+CFLAGS_REMOVE_udivdi3.o := $(LTO_CFLAGS)
 
 MINIMAL=
 
diff --git a/arch/powerpc/lib/udivdi3.c b/arch/powerpc/lib/udivdi3.c
new file mode 100644
index 00000000000..deb0b5d49e1
--- /dev/null
+++ b/arch/powerpc/lib/udivdi3.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 64-bit unsigned division and modulo for the private libgcc
+ *
+ * GCC calls __udivdi3() and __umoddi3() for '/' and '%' on unsigned 64-bit
+ * operands on 32-bit PowerPC. udivmod64() is __div64_32() from lib/div64.c
+ * with a 64-bit divisor: its shift-and-subtract loop already works on one,
+ * and the step that first divides the high word now only runs when the
+ * divisor fits into 32 bits. Like __div64_32(), the functions are not
+ * instrumented, as tracing code may divide. The source file copyrights are
+ * as follows:
+ *
+ *   Copyright (C) 2003 Bernardo Innocenti <[email protected]>
+ *
+ *   Based on former do_div() implementation from asm-parisc/div64.h:
+ *     Copyright (C) 1999 Hewlett-Packard Co
+ *     Copyright (C) 1999 David Mosberger-Tang <[email protected]>
+ */
+
+#include <linux/types.h>
+
+static notrace u64 udivmod64(u64 n, u64 base, u64 *rem)
+{
+       u64 b = base;
+       u64 res, d = 1;
+       u32 high = n >> 32;
+
+       /* Reduce the thing a bit first */
+       res = 0;
+       if (!(base >> 32) && high >= base) {
+               high /= (u32)base;
+               res = (u64)high << 32;
+               n -= (u64)(high * (u32)base) << 32;
+       }
+
+       while ((s64)b > 0 && b < n) {
+               b = b + b;
+               d = d + d;
+       }
+
+       do {
+               if (n >= b) {
+                       n -= b;
+                       res += d;
+               }
+               b >>= 1;
+               d >>= 1;
+       } while (d);
+
+       *rem = n;
+       return res;
+}
+
+/**
+ * __udivdi3() - divide two 64-bit unsigned integers
+ *
+ * @n:         dividend
+ * @d:         divisor
+ * Return:     quotient
+ */
+notrace u64 __udivdi3(u64 n, u64 d)
+{
+       u64 rem;
+
+       return udivmod64(n, d, &rem);
+}
+
+/**
+ * __umoddi3() - remainder of the division of two 64-bit unsigned integers
+ *
+ * @n:         dividend
+ * @d:         divisor
+ * Return:     remainder
+ */
+notrace u64 __umoddi3(u64 n, u64 d)
+{
+       u64 rem;
+
+       udivmod64(n, d, &rem);
+
+       return rem;
+}
-- 
2.54.0 (Apple Git-157)

Reply via email to