[git commit] libbb: use narrow isqrt() when 64-bit one is not needed (only "factor" uses it)

Denys Vlasenko vda.linux at googlemail.com
Wed Feb 4 18:47:11 UTC 2026


commit: https://git.busybox.net/busybox/commit/?id=e33bd4aaa2d6558fc1b008cd2e3bcc5c4f392be2
branch: https://git.busybox.net/busybox/log/?h=master

function                                             old     new   delta
isqrt_ull                                              -      84     +84
create_J                                            1809    1808      -1
isqrt                                                106      38     -68
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/2 up/down: 84/-69)             Total: 15 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 coreutils/factor.c |  2 +-
 include/libbb.h    |  7 ++++++-
 libbb/isqrt.c      | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/coreutils/factor.c b/coreutils/factor.c
index 90e9ed761..4df523f28 100644
--- a/coreutils/factor.c
+++ b/coreutils/factor.c
@@ -136,7 +136,7 @@ static void factorize(wide_t N);
 
 static half_t isqrt_odd(wide_t N)
 {
-	half_t s = isqrt(N);
+	half_t s = isqrt_ull(N);
 	/* s^2 is <= N, (s+1)^2 > N */
 
 	/* If s^2 in fact is EQUAL to N, it's very lucky.
diff --git a/include/libbb.h b/include/libbb.h
index aacff42ee..7cca925b9 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -408,7 +408,12 @@ unsigned FAST_FUNC bb_popcnt_long(unsigned long m);
 #define bb_popcnt_long(m) bb_popcnt_32(m)
 #endif
 
-unsigned long FAST_FUNC isqrt(unsigned long long N);
+unsigned FAST_FUNC isqrt(unsigned long N);
+#if LLONG_MAX > LONG_MAX
+unsigned long FAST_FUNC isqrt_ull(unsigned long long N);
+#else
+# define isqrt_ull(N) isqrt(N)
+#endif
 
 unsigned long long monotonic_ns(void) FAST_FUNC;
 unsigned long long monotonic_us(void) FAST_FUNC;
diff --git a/libbb/isqrt.c b/libbb/isqrt.c
index 817b7d036..507c7d637 100644
--- a/libbb/isqrt.c
+++ b/libbb/isqrt.c
@@ -9,15 +9,42 @@
 #ifndef ISQRT_TEST
 # include "libbb.h"
 #else
-/* gcc -DISQRT_TEST -Wall -O2 isqrt.c -oisqrt && ./isqrt $((RANDOM*RANDOM)) */
+// gcc -DISQRT_TEST -Wall -Os -ffunction-sections isqrt.c -S -fverbose-asm
+// gcc -DISQRT_TEST -Wall -Os -ffunction-sections isqrt.c -c
+// gcc -DISQRT_TEST -Wall -Os -ffunction-sections isqrt.c -oisqrt && ./isqrt $((RANDOM*RANDOM))
 # include <stdlib.h>
 # include <stdio.h>
 # include <time.h>
+# include <limits.h>
 # define FAST_FUNC /* nothing */
 #endif
 
 /* Returns such x that x+1 > sqrt(N) */
-unsigned long FAST_FUNC isqrt(unsigned long long N)
+
+/* Stolen from kernel code */
+unsigned FAST_FUNC isqrt(unsigned long x)
+{
+	unsigned long y = 0;
+	unsigned long m = 1UL << (8*sizeof(x) - 2);
+	// ^^^^^ should be m = 1UL << ((fls(x) - 1) & ~1UL);
+	// but calculating fls() would take time and code
+
+	do {
+		unsigned long b = y + m;
+		y >>= 1;
+		if (x >= b) {
+			x -= b;
+			y += m;
+		}
+		m >>= 2;
+	} while (m);
+        return y;
+}
+
+#if LLONG_MAX > LONG_MAX
+# if defined(i386)
+/* Smaller code on this register-starved arch */
+unsigned long FAST_FUNC isqrt_ull(unsigned long long N)
 {
 	unsigned long x;
 	unsigned shift;
@@ -33,15 +60,40 @@ unsigned long FAST_FUNC isqrt(unsigned long long N)
 	} while ((int)shift >= 0);
 	return x;
 }
+# else
+/* Stolen from kernel code */
+unsigned long FAST_FUNC isqrt_ull(unsigned long long x)
+{
+	unsigned long long y = 0;
+	unsigned long long m = 1ULL << (8*sizeof(x) - 2);
+	// ^^^^^ should be m = 1ULL << ((fls(x) - 1) & ~1ULL);
+	// but calculating fls() would take time and code
+
+	do {
+		unsigned long long b = y + m;
+		y >>= 1;
+		if (x >= b) {
+			x -= b;
+			y += m;
+		}
+		m >>= 2;
+	} while (m);
+        return y;
+}
+# endif
+#endif /* wide version needed */
 
 #ifdef ISQRT_TEST
+# if LLONG_MAX == LONG_MAX
+#  define isqrt_ull(N) isqrt(N)
+# endif
 int main(int argc, char **argv)
 {
 	unsigned long long n = argv[1] ? strtoull(argv[1], NULL, 0) : time(NULL);
 	for (;;) {
 		unsigned long h;
 		n--;
-		h = isqrt(n);
+		h = isqrt_ull(n);
 		if (!(n & 0xffff))
 			printf("isqrt(%llx)=%lx\n", n, h);
 		if ((unsigned long long)h * h > n) {


More information about the busybox-cvs mailing list