[PATCH v2 1/3] lib: extract 32bit mul/div helpers from nios2 into GENERIC_LIB

From: Simon Schuster

Date: Tue Nov 03 2026 - 08:39:11 EST


From: Dinh Nguyen <dinguyen@xxxxxxxxxx>

As suggested in 426c73b7-b4dc-4b68-9aa5-6145a13280bc@xxxxxxxxxxxxxxxx,
the software emulation of div/mul/mod on 32-bit platforms from nios2
might be of use to other smaller architectures as well when trying to
drop their libgcc dependency (e.g., arc, openrisc).
Therefore, extract and add them to lib/, guarded by respective
CONFIG_GENERIC_LIB_* options.

Symbols covered:
s32 __mulsi3(s32 a, s32 b);
s32 __divsi3(s32 a, s32 b);
s32 __udivsi3(s32 a, s32 b);
s32 __modsi3(s32 a, s32 b);
s32 __umodsi3(s32 a, s32 b);

Suggested-by: Arnd Bergmann <arnd@xxxxxxxx>
Signed-off-by: Dinh Nguyen <dinguyen@xxxxxxxxxx>
Co-developed-by: Simon Schuster <schuster.simon@xxxxxxxxxxxxxxxxxx>
Signed-off-by: Simon Schuster <schuster.simon@xxxxxxxxxxxxxxxxxx>
---
include/linux/libgcc.h | 10 ++++++
lib/Kconfig | 15 ++++++++
lib/Makefile | 10 ++++++
lib/divmod.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/mulsi3.c | 24 +++++++++++++
5 files changed, 153 insertions(+)

diff --git a/include/linux/libgcc.h b/include/linux/libgcc.h
index 0d68f9d6a6a7e..f5db35a9bc146 100644
--- a/include/linux/libgcc.h
+++ b/include/linux/libgcc.h
@@ -7,6 +7,7 @@
#define __LIB_LIBGCC_H

#include <asm/byteorder.h>
+#include <linux/types.h>

typedef int word_type __attribute__ ((mode (__word__)));

@@ -34,6 +35,15 @@ long long notrace __lshrdi3(long long u, word_type b);
long long notrace __muldi3(long long u, long long v);
word_type notrace __ucmpdi2(unsigned long long a, unsigned long long b);

+/*
+ * Software 32-bit multiply for cores built without the hardware multiplier.
+ */
+s32 notrace __mulsi3(s32 a, s32 b);
+s32 notrace __divsi3(s32 a, s32 b);
+s32 notrace __udivsi3(s32 a, s32 b);
+s32 notrace __modsi3(s32 a, s32 b);
+s32 notrace __umodsi3(s32 a, s32 b);
+
#ifdef CONFIG_HAVE_ARCH_LIBGCC_H
#include <asm/libgcc.h>
#endif
diff --git a/lib/Kconfig b/lib/Kconfig
index 1e42f5167c68b..16c2f2008b82c 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -611,6 +611,21 @@ config GENERIC_LIB_CMPDI2
config GENERIC_LIB_UCMPDI2
bool

+config GENERIC_LIB_DIVSI3
+ bool
+
+config GENERIC_LIB_MODSI3
+ bool
+
+config GENERIC_LIB_UDIVSI3
+ bool
+
+config GENERIC_LIB_UMODSI3
+ bool
+
+config GENERIC_LIB_MULSI3
+ bool
+
config GENERIC_LIB_DEVMEM_IS_ALLOWED
bool

diff --git a/lib/Makefile b/lib/Makefile
index a2afe954805cb..352a012baecba 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -337,6 +337,16 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o
obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
+obj-$(CONFIG_GENERIC_LIB_MULSI3) += mulsi3.o
+
+divmod-needed := $(or \
+ $(CONFIG_GENERIC_LIB_DIVSI3),\
+ $(CONFIG_GENERIC_LIB_MODSI3),\
+ $(CONFIG_GENERIC_LIB_UDIVSI3),\
+ $(CONFIG_GENERIC_LIB_UMODSI3))
+obj-$(divmod-needed) += divmod.o
+
+
obj-$(CONFIG_OBJAGG) += objagg.o

# pldmfw library
diff --git a/lib/divmod.c b/lib/divmod.c
new file mode 100644
index 0000000000000..cc1df6f9978ce
--- /dev/null
+++ b/lib/divmod.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright Altera Corporation (C) 2026. All rights reserved.
+ */
+
+#include <linux/libgcc.h>
+#include <linux/export.h>
+#include <linux/types.h>
+
+static u32 udivmodsi4(u32 num, u32 den, int modwanted)
+{
+ u32 bit = 1;
+ u32 res = 0;
+
+ while (den < num && bit && !(den & (1U << 31))) {
+ den <<= 1;
+ bit <<= 1;
+ }
+
+ while (bit) {
+ if (num >= den) {
+ num -= den;
+ res |= bit;
+ }
+ bit >>= 1;
+ den >>= 1;
+ }
+
+ return modwanted ? num : res;
+}
+
+
+#ifdef CONFIG_GENERIC_LIB_DIVSI3
+s32 __divsi3(s32 a, s32 b)
+{
+ int neg = 0;
+ s32 res;
+
+ if (a < 0) {
+ a = -a;
+ neg = !neg;
+ }
+
+ if (b < 0) {
+ b = -b;
+ neg = !neg;
+ }
+
+ res = udivmodsi4(a, b, 0);
+ if (neg)
+ res = -res;
+
+ return res;
+}
+EXPORT_SYMBOL(__divsi3);
+#endif
+
+#ifdef CONFIG_GENERIC_LIB_MODSI3
+s32 __modsi3(s32 a, s32 b)
+{
+ int neg = 0;
+ s32 res;
+
+ if (a < 0) {
+ a = -a;
+ neg = 1;
+ }
+
+ if (b < 0)
+ b = -b;
+
+ res = udivmodsi4(a, b, 1);
+ if (neg)
+ res = -res;
+
+ return res;
+}
+EXPORT_SYMBOL(__modsi3);
+#endif
+
+#ifdef CONFIG_GENERIC_LIB_UDIVSI3
+s32 __udivsi3(s32 a, s32 b)
+{
+ return udivmodsi4(a, b, 0);
+}
+EXPORT_SYMBOL(__udivsi3);
+#endif
+
+#ifdef CONFIG_GENERIC_LIB_UMODSI3
+s32 __umodsi3(s32 a, s32 b)
+{
+ return udivmodsi4(a, b, 1);
+}
+EXPORT_SYMBOL(__umodsi3);
+#endif
diff --git a/lib/mulsi3.c b/lib/mulsi3.c
new file mode 100644
index 0000000000000..6596a6598de05
--- /dev/null
+++ b/lib/mulsi3.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright Altera Corporation (C) 2026. All rights reserved.
+ */
+
+#include <linux/libgcc.h>
+#include <linux/export.h>
+#include <linux/types.h>
+
+s32 __mulsi3(s32 a, s32 b)
+{
+ s32 res = 0;
+ u32 cnt = a;
+
+ while (cnt) {
+ if (cnt & 1)
+ res += b;
+
+ b <<= 1;
+ cnt >>= 1;
+ }
+
+ return res;
+}
+EXPORT_SYMBOL(__mulsi3);

--
2.39.5